#include <stdio.h>
int main() {
int t;
scanf("%d", &t);
printf("%d", t);
return 0;
}
我使用ideone.com编译上述C代码时出现以下警告:
“prog.c: 在函数‘main’中:
prog.c:5: 警告:忽略带有属性warn_unused_result的‘scanf’的返回值”
请问有人可以帮助我理解这个警告吗?该警告表示忽略了scanf的返回值。
#include <stdio.h>
int main() {
int t;
scanf("%d", &t);
printf("%d", t);
return 0;
}
scanf
的返回值在大多数情况下不应该被忽略,因此他们给它添加了一个属性,告诉编译器给出警告。#include <stdio.h>
int main() {
int t;
if (scanf("%d", &t) == 1) {
printf("%d", t);
} else {
printf("Failed to read integer.\n");
}
return 0;
}
scanf
的返回值是一个坏主意。函数scanf
已经通过gcc函数属性显式声明,如果您丢弃其返回值,则会触发此警告。(void)scanf("%d",&t);
void
强制类型转换是通用标准,表示“故意未使用此结果”,被广泛认可,除了 GCC 之外。因为约20年前有人(Stallmann?)决定不应该有忽略 warn_unused_result
的方式。 - fuz(void)! ...
可以用来避免 gcc
警告 (@kraffenetti @AffluentOwl),参见 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425#c34。 - rivy我尝试了您的示例代码,使用的是gcc(Ubuntu 4.4.3-4ubuntu5.1)4.4.3版本。 只有在进行优化编译时(例如使用 -O2 或 -O3 选项),才会发出警告。 请求所有警告(-Wall选项)不起作用。 将任意类型转换为void指针的经典习惯用法没有作用,它不能抑制该警告。
我可以通过编写以下内容来消除警告:
if(scanf("%d",&t)){};
这段代码虽然可行,但对我来说有点晦涩。使用Empty {}可以避免另一个警告-Wempty-body。
if
结尾处带有分号的代码,我会认为它可能是一个 bug。至少需要一个解释性注释。 - Craig McQueen请执行以下操作:
int main() {
int t;
int unused __attribute__((unused));
unused = scanf("%d",&t);
printf("%d",t);
return 0;
}
u = scanf("%d", &t);
assert(u == 1);
#define NDEBUG
关闭断言,则会得到一个 -Wunused-but-set-variable
。然后,您可以通过以下两种方式之一关闭此第二个警告:
gcc
命令行中添加 -Wno-unused-but-set-variable
,或者int u __attribute__((unused));
#define igr(x) {__typeof__(x) __attribute__((unused)) d=(x);}
double __attribute__ ((warn_unused_result)) fa(void) {return 2.2;}
igr(fa());
另请参阅此答案
assert
,确实是一种滥用。上面的答案列出了各种处理“未使用scanf返回值”的选项,这是主要问题。assert
部分说明如果调试。我认为将所有选项“写下来”作为教育性信息回答是很好的,这样人们就可以学习所有工具并做出明智的选择。这也是我认为您的评论有帮助的原因,这样人们就不会错误地滥用assert()
来完成不适合它的工作。谢谢。 - DrBecoIGUR()
函数。非常丑陋,但仍然有些可移植性。(对于不理解 inline
的旧编译器,只需像往常一样定义 #define inline /*nothing*/
即可。)#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
inline void IGUR() {} /* Ignore GCC Unused Result */
void IGUR(); /* see https://dev59.com/W2Qo5IYBdhLWcg3wMs-2#16245669 */
int
main(int argc, char **argv)
{
char buf[10*BUFSIZ];
int got, fl, have;
fl = fcntl(0, F_GETFL);
fcntl(0, F_SETFL, fl|O_NONBLOCK);
have = 0;
while ((got=read(0, buf, sizeof buf))>0)
{
IGUR(write(1, buf, got));
have = 1;
}
fcntl(0, F_SETFL, fl);
return have;
}
stdin
复制到 stdout
,直到所有等待的输入都被读取完毕,如果没有任何内容,则返回 true
(0),否则返回false
(1)。 (它可以防止在像 bash
中的 while read -t1 away; do :; done
中出现1秒的延迟。)
在 -Wall
(Debian Jessie)下编译时不会出现警告。
编辑:IGUR()
也需要在没有 inline
的情况下定义,以便它可以供链接器使用。否则,在使用 cc -O0
时可能会失败。请参见:https://dev59.com/W2Qo5IYBdhLWcg3wMs-2#16245669
编辑2:新版本的 gcc
要求在 void
前放置 inline
。
bash
中可以写成read -t0.01 away
,这样就只会添加100分之一秒的延迟。不应该低于这个值,因为如果进程被减速(例如在strace
下运行),计时器可能会在read()
执行之前触发,从而导致read
无效。 - Tino实际上这取决于您的需求,如果您只想禁用编译器警告,可以通过强制转换忽略函数的返回值,或者您也可以处理它,scanf
函数的含义是用户输入的数量。
==== 更新 ====
您可以使用
(void) scanf("%d",&t);
来忽略scanf
的返回值。
有人能帮我理解这个警告吗?
不行,但是我可以为警告抑制的恐怖做出贡献。为了积极地将返回值丢弃,优雅的方法是将我们的语句包装在一个易于理解的lambda函数中,像这样:
[&]{ return scanf("%d", &t); }();
scanf和printf是返回值的函数,通常在这些函数中返回的是读取或写入的字符数。如果发生错误,您也可以通过返回代码捕获错误。 良好的编程实践是查看返回值,但是我从未见过有人查看printf的返回值...
如果您想要消除警告,您可能可以更改编译器的严重性。
只需使用一个 if() 包围和一个空块,终止的分号必须在下一行(以防止额外的警告)
#include <stdio.h>
main (int argc, char const *argv[]) {
...
if ( scanf("%d",&n) )
;
...
return 0;
}
printf
失败,你几乎无能为力。但是scanf
可能很容易失败。只需要输入一个字母'A'
而不是整数,你的程序就会崩溃。 - Evan Teranwarn_unused_result
函数属性的文档,如果你正在使用GCC(Ideone使用的就是),并且查看你的stdio.h
头文件,你将会看到scanf
及其相关函数被标记为warn_unused_result
属性,但是printf
及其相关函数则没有。 - Adam Rosenfield