最近,我开始使用lint进行静态代码分析。有时候,我会遇到以下问题之一的警告。比如说,如果我有下面这个函数:
uint32_t foo( void );
假设我故意忽略函数的返回值。 为了让警告消失,可以这样写:
(void) foo();
我的问题是,像这样编写代码的“适当”方式是什么,我应该继续像往常一样,因为编译器没有抱怨,还是应该使用void来表明清晰,以便其他代码维护者知道我故意忽略了返回值。
当我看到带有void的代码时,它对我来说看起来相当奇怪...
最近,我开始使用lint进行静态代码分析。有时候,我会遇到以下问题之一的警告。比如说,如果我有下面这个函数:
uint32_t foo( void );
假设我故意忽略函数的返回值。 为了让警告消失,可以这样写:
(void) foo();
我的问题是,像这样编写代码的“适当”方式是什么,我应该继续像往常一样,因为编译器没有抱怨,还是应该使用void来表明清晰,以便其他代码维护者知道我故意忽略了返回值。
当我看到带有void的代码时,它对我来说看起来相当奇怪...
通常的方法是直接调用foo();
而不需要强制转换为(void)
。
从来没有忽略过printf()
的返回值的人,请投掷第一块石头。
UNREFERENCED_PARAMETER
用于未使用的参数;您可以为其他表达式创建类似的宏。 - user541686write()
,或 fscanf(...,"%*s\n")
或 strtol()
返回值不重要,只想要 [也许] 移动文件指针的副作用。)(void)
不再起作用。{ssize_t ignore; ignore=write(...);}
抛出另一个警告(赋值但未使用)。write(...)+1
抛出另一个警告(计算结果未使用)。(void)(write(...) + 1)
。+0
不起作用。)if(write(...));
也应该可以工作。不要忘记加上尾部的分号;
。 - con-f-use(void)!foo(); /*works with gcc4.6+ */
最好放在宏或内联函数中。最好使用支持“不能忽略”的工具。有些返回是信息性的,而其他则是关键的。 - artless noise使用Clang和GCC编译器之一实现此操作的一种方法是使用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
指令,以便可以在代码的其他地方触发警告。这样做可以使日后阅读源代码的人更容易看出此代码块的作用。
一个稍微更加 "美观" 的方式来指示未使用的结果是这样的:
/**
* 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(const T & /* unused result */) {}
static inline void ignore_result(ssize_t unused) { (void)unused; }
,因为在C中您不能像在C++中那样省略参数标识符。此外,如果您在gcc中没有添加 static
,且禁用优化,则会生成链接器错误。 - Watcom(void)
或停用 printf
的检查。现在,您有几个选项可以以可读的方式执行此操作。例如,我通常会将函数包装在一个新函数中。void printf_unchecked(const char* format, ...)
这里是不太好的类型转换发生地。也许在这种情况下,使用预处理器宏更为实际,因为有可变参数...
(void)
转换,但请返回+1。感谢您理解问题并提供不同的方法。 - stefan#define UNCHECKED_RV(function_call) (void)function_call
这种解决方案或者使用 function_unchecked() 包装器的好处在于,它非常清楚地表明你有意忽略了返回值。将其转换为 void 并不那么清晰。 - bpeikes我喜欢使用以下标志编译我的代码:
$gcc prog1.c -o prog1.x -Wall -Wextra -ansi -pedantic-errors -g -O0 -DDEBUG=1
-Wunused-result
,我不喜欢添加另一个标志:-Wno-unused-result
(如果你这样做,那是一种解决方案)。(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需要更新以解决此问题。
因此,为了以一种非常兼容的方式消除警告,这里有一个不错的宏
:
/** Turn off -Wunused-result for a specific function call */
#define igr(M) if(1==((long)M)){;}
然后像这样调用:
igr(PL_get_chars(t, &s, CVT_VARIABLE));
这是一种简洁的写法,任何编译器都可以消除这段代码。下面是我喜欢的编辑器vi
的图片:左边窗口没有igr()
;中间窗口使用了igr()
;右边窗口是源代码。
你可以看到,它们完全相同,这是一个完全无害的代码,让C做gcc不允许的事情:忽略返回值。
比较1==...
只是为了避免splint警告,这个条件不是BOOL
。GCC并不在意。根据函数的不同,你可能会得到一个cast
警告。我用这个宏忽略了一个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
请参见此答案
__attribute__ ((warn_unused_result))
。 - DrBeco-Wno-unused-result
。 - qwrgnulib提供了以下内容:
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
__warn_unused_result__
是我的情况。 - sqr163#define _unused(x) ((void)(x))
然后在你的例子中,你会有:
val = foo();
_unused(val);
#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 预处理器的 # 指令类似的指令来标记具有可选返回值的函数。
#pragma optresult
可以立即放置在返回可选结果的函数的定义之前。然后 LINT 就会认识到该函数返回可以被忽略的结果;如果结果被忽略,LINT 不会给出错误信息。
在某些情况下编写忽略返回值的代码是完全合法且可接受的。下面的程序很少有理由检查printf()的返回值。
int main(void) {
printf("Hello world\n");
return 0;
}