最近、静的コード分析にlintを使用し始めました。私が時々受け取る警告の1つは、この問題に関するものです。たとえば、次の機能があるとします。
uint32_t foo( void );
そして、関数の戻り値を慎重に無視するとしましょう。警告を消すには、次のように書くことができます
(void) foo();
私の質問は、このようなコードを書くための「適切な」方法は何ですか、コンパイラがそれについて文句を言わないので、私はいつものように続けるべきですか、または明確にするためにボイドを使用する必要がありますので、他のコードメンテナは知っています私は戻り値を慎重に無視したこと。
このようなコードを(voidで)見ると、かなり奇妙に見えます...
一般的な方法は、_(void)
_にキャストせずにfoo();
を呼び出すだけです。
printf()
の戻り値を無視したことがない彼は、最初の石を投げました。
個人的には「未使用」の警告が好きですが、時々、それらを無視しなければならない場合があります(例えば、ユーザーへのwrite()
、またはfscanf(...,"%*s\n")
またはstrtol()
戻り値は重要ではなく、ファイルポインターを[多分]移動させる副作用が欲しいだけです。)
Gcc 4.6では、非常に注意が必要です。
(void)
_へのキャストは機能しなくなりました。{ssize_t ignore; ignore=write(...);}
は、別の警告(assigned-not-used)をスローします。write(...)+1
は、さらに別の警告(計算値未使用)をスローします。これらを抑制するための唯一の良い(い場合)方法は、戻り値を、コンパイラが無視できることに同意するものに変換することです。
例:_(void)(write(...)+1)
_。
これは明らかに進行中です。 (そして_+0
_は動作しません、ところで。)
ClangおよびGCCコンパイラでこれを行う1つの方法は、pragma
を使用することです。
/* ... */
#pragma GCC diagnostic Push
#pragma GCC diagnostic ignored "-Wunused-result"
foo(); /* this specific unused-result warning gets ignored during compilation */
#pragma GCC diagnostic pop
/* ... */
Push
-pop
の組み合わせはignored
ディレクティブをラップするため、コードの他の場所で警告をトリガーできます。このコードブロックが何をするのか、ソースコードを読んでいる人が簡単にわかるはずです。
未使用の結果を示すもう1つの「美しい」方法は次のとおりです。
/**
* Wrapping your function call with ignore_result makes it more clear to
* readers, compilers and linters that you are, in fact, ignoring the
* function's return value on purpose.
*/
static inline void ignore_result(long long int unused_result) {
(void) unused_result;
}
...
ignore_result(foo());
C++
、これは次のように拡張できます。
template<typename T>
inline void ignore_result(T /* unused result */) {}
静的コードチェッカーが有用であるためには、無視された戻り値も報告する必要があります。これにより、エラーの追跡が困難になったり、エラー処理が欠落したりする可能性が非常に高くなります。
したがって、(void)
またはprintf
のチェックを無効にします。これで、読みやすい方法でそれを行うためのいくつかのオプションがあります。たとえば、新しい関数の中に関数をラップするために使用します
void printf_unchecked(const char* format, ...)
それほど素敵なキャストは行われません。おそらく、可変引数のために、この場合はプリプロセッサマクロを使用して行う方が実用的です...
私はフラグを使用してコードをコンパイルするのが好きです:
_$gcc prog1.c -o prog1.x -Wall -Wextra -ansi -pedantic-errors -g -O0 -DDEBUG=1
_
_-Wunused-result
_を避けるために、別のフラグを追加するという考えが好きではありません:_-Wno-unused-result
_(もしそうなら、それが1つの解決策です)。
私は以前、いくつかの関数について_(void)
_にキャストしていました(printf
やその他の有名なものではなく、コンパイラーはそれらについて警告しないため、奇妙なものだけです)。 _(void)
_へのキャストが機能しなくなりました(GCC 4.7.2)
面白いスプリントのアドバイス:
_Result returned by function call is not used. If this is intended,
can cast result to (void) to eliminate message. (Use -retvalother to
inhibit warning)
_
しかし、これはもはや解決策ではありません。 Splintはこの問題に関する更新が必要です。
したがって、非常に互換性のある方法で警告を取り除くために、良いMACRO
があります:
_/** Turn off -Wunused-result for a specific function call */
#define igr(M) if(1==((int)M)){;}
_
そして、次のように呼び出します:
_igr(PL_get_chars(t, &s, CVT_VARIABLE));
_
そのきれいな外観、およびすべてのコンパイラはコードを排除します。好みのエディターの写真の下にvi
:左ウィンドウ、なしigr()
; igr()
;を使用した中央ウィンドウ右ウィンドウ、ソース。
まったく同じ無害なコードを見ることができます。これは、gccができないことをCに許可します。戻りコードを無視します。
比較_1==...
_は、この条件がBOOL
でないことを示すスプリント警告を回避するためにのみ必要です。 GCCはそれほど気にすることができませんでした。関数によっては、cast
警告が表示される場合があります。このMACROでdouble
を無視してテストしましたが、それは良かったのですが、どういうわけか私は完全には納得していません。特に、関数がポインターまたはより複雑なものを返す場合。
この場合、次のものも必要になります。
_#define pigr(M) if(NULL==((void *)M)){;}
_
最後に:_{;}
_は、_-Wempty-body
_警告(「if」ステートメントの空のボディを囲む中括弧を推奨)のために必要です。
そして、(最後の)関数呼び出し後の_;
_は(厳密には)必要ではありませんが、その良い習慣です。コード行をより均質にし、すべてが_;
_で終わるようにします。 (これはNOP
ニーモニックとして翻訳され、最適化後に消えます)。
コンパイラを実行しても、警告やエラーは発生しません。 splint
を実行すると:
_$ splint ./teste.c -I/usr/lib/swi-prolog/include/ -strict-lib
Splint 3.1.2 --- 20 Feb 2009
Finished checking --- no warnings
_
this answer も参照してください
gnulibには以下があります: http://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/ignore-value.h
/* Normally casting an expression to void discards its value, but GCC
versions 3.4 and newer have __attribute__ ((__warn_unused_result__))
which may cause unwanted diagnostics in that case. Use __typeof__
and __extension__ to work around the problem, if the workaround is
known to be needed. */
#if 3 < __GNUC__ + (4 <= __GNUC_MINOR__)
# define ignore_value(x) \
(__extension__ ({ __typeof__ (x) __x = (x); (void) __x; }))
#else
# define ignore_value(x) ((void) (x))
#endif
通常、無視したい値の関数はあまり多くありません。 Splintは、たとえば、特定の関数の戻り値が無視されることを知らせる特別なコメントを追加できますmightは無視されます。残念ながら、これにより、その特定の機能に関連するすべての「無視される戻り値」警告が無効になります。
Splint-cleanプログラムの例を次に示します。
#include <stdio.h>
FILE /*@alt void@*/ *fopen(const char *path, const char *mode);
static int /*@alt void@*/ test(void)
{
printf( "test called\n" );
fopen( "test", "a" );
return 0;
}
int main(void)
{
test();
return 0;
}
不快な部分は、どこかにコメントを付けてシステム関数に追加のプロトタイプを追加する必要があることです。
ところで、デフォルトではSplintはprintf
およびその他のlibc関数の未使用の戻り値について文句を言いません。ただし、より厳密なモードをアクティブにできます。
LINTは同様のものを許可しますが、私はそれを使用したことがありません。これは、ドキュメントに書かれていることです。
LINTでは、Cプリプロセッサの#directivesに似たディレクティブを使用して、オプションの戻り値で関数をマークできます。
#pragma optresult
オプションの結果を返す関数の定義の直前に配置できます。 LINTは、この関数が無視できる結果を返すことを認識します。結果が無視される場合、LINTはエラーメッセージを表示しません。
他の解決策は、実際に値を使用することです。その後、マクロを使用してunused variable
警告を削除できます。
#define _unused(x) ((void)(x))
次に、あなたの例では、あなたが持っているでしょう:
val = foo();
_unused(val);