【问题标题】:Warning: ignoring return value of 'scanf', declared with attribute warn_unused_result警告:忽略使用属性 warn_unused_result 声明的“scanf”的返回值
【发布时间】:2011-11-08 11:21:37
【问题描述】:
#include <stdio.h>

int main() {
    int t;
    scanf("%d", &t);
    printf("%d", t);
    return 0;
}

我使用 ideone.com 编译了上面的 C 代码并弹出以下警告:

prog.c:在函数“main”中:
prog.c:5:警告:忽略返回值 'scanf',用属性 warn_unused_result 声明

有人可以帮我理解这个警告吗?

【问题讨论】:

    标签: c


    【解决方案1】:

    谁能帮我理解这个警告?

    不,但这是我对警告压制的恐惧的贡献。为了主动将返回值抛到窗外,优雅要求将我们的语句包装在一个可理解的 lambda 函数中,如下所示:

    [&]{ return scanf("%d", &t); }();
    

    我很抱歉。

    【讨论】:

    • 我喜欢这种优雅;它类似于 javascript 中的 IIFE 习语。但是像 IIFE 一样,注意不要省略尾随的 ()!我认为在这种情况下可能会收到警告,但我没有收到 -Wall 警告。
    【解决方案2】:

    由于没有参数的函数在 C 中有效,因此您可以执行以下操作:

    #include <stdio.h>
    
    static inline void ignore_ret() {}
    
    int main() {
       int t;
       ignore_ret(scanf("%d", &t));
       return 0;
    }
    

    【讨论】:

      【解决方案3】:

      解决此问题的一种方法是IGUR() 函数,如下所示。极其丑陋,但仍然有些便携。 (对于不理解 inline 的旧编译器,只需 #define inline /*nothing*/,就像往常一样。)

      #include <stdio.h>
      #include <unistd.h>
      #include <fcntl.h>
      
      inline void IGUR() {}  /* Ignore GCC Unused Result */
      void IGUR();  /* see https://stackoverflow.com/a/16245669/490291 */
      
      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)。 (它可以防止 while read -t1 away; do :; donebash 中出现 1 秒延迟。)

      -Wall (Debian Jessie) 下无警告地编译。

      编辑:IGUR() 也需要在没有 inline 的情况下定义,以便它可以用于链接器。否则cc -O0 可能会失败。见:https://stackoverflow.com/a/16245669/490291

      Edit2:较新的gcc 要求inlinevoid 之前。

      【讨论】:

      • 我刚刚发现你可以在bash 中写入read -t0.01 away,这只会增加百分之一秒的延迟。您不应低于此值,因为如果进程变慢(例如在strace 下运行),计时器可能会在read() 执行之前触发,因此read 什么也不做。
      【解决方案4】:

      其实看你需要,如果你只是想关闭编译器的警告,你可以通过强制转换忽略函数的返回值或者直接处理,scanf的意思function 是用户输入的计数。

      ==== 更新====

      你可以使用

      (void) scanf("%d",&amp;t);

      忽略scanf的返回值

      【讨论】:

      • 减一表示提倡无视警告。比你我更聪明的人对他们负责。他们告诉你有用的东西。关注它们会带来更好的代码质量。忽视它们后果自负,请不要提倡他人忽视或压制它们
      • 无论如何,我知道警告在哪里,我知道它会对我的程序造成伤害。有些项目可能会将警告视为错误。我的意思是摆脱警告
      • 通过处理错误来消除警告,而不是躲避它们......
      • 这对 gcc 5.4.x 不起作用(不再)。在函数前面放置 (void) cast 时,我仍然收到相同的警告。
      • @WoodrowDouglass 警告不是错误。当您看到警告时,您会检查线路并确定它是否正常。在某些情况下,您确实确实想忽略 scanf 的返回,因此无法删除该行的警告,坦率地说,是愚蠢的。您不希望已经检查和确定的安全代码发出警告,因为这样会增加编译器输出中的噪声/信号比。
      【解决方案5】:

      阅读此页面上的所有答案和 cmets 后,我没有看到这些避免警告的其他选项:

      当使用gcc编译时,您可以添加到命令行:

      gcc -Wall -Wextra -Wno-unused-result proc.c -o prog.x

      另一种选择是使用-O0,因为“优化级别零”会忽略警告。

      在使用gcc 编译时,使用强制转换为(void) 只是无用

      如果调试您的代码,您始终可以使用 assert(),如下例所示:

      u = scanf("%d", &t);
      assert(u == 1);
      

      但是现在,如果您通过#define NDEBUG 关闭断言,您将获得-Wunused-but-set-variable。然后,您可以通过以下两种方式之一关闭此第二个警告

      1. -Wno-unused-but-set-variable 添加到您的 gcc 命令行,或
      2. 用属性声明变量: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());
      

      另见this answer

      【讨论】:

      • assert 是一种滥用。如果assert 失败,则程序员编写了错误代码。如果我在你的程序中输入A,你的代码就会中止,因此它会失败。
      • 感谢您的评论。我同意,如果您使用assert,因为您对我的回答有误解,确实是abuse。上面的答案列出了处理“未使用的scanf返回”的各种选项,这是重点。 assert 部分声明如果调试。我认为将所有选项“写下”作为具有教育意义的答案是很好的,这样人们就可以学习所有工具并做出有根据的选择。这也是我认为您的评论很有帮助的原因,因此人们也不会错误地阅读此内容并滥用assert() 来完成不适合它的工作。谢谢。
      【解决方案6】:

      你的 libc 的作者已经决定在大多数情况下不应该忽略 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。也许很多人不知道,但 printf 也返回一个值,而且他似乎没有抱怨那里的丢弃结果。
      • 编译器只会对具有该特定属性的函数发出该警告。如果printf 失败,您无能为力。但是scanf 很容易失败。我需要做的就是输入 'A' 而不是整数,然后你的程序就坏了。
      • 感谢大家的回答。这个问题是今天才出现的。我之前已经多次使用同一个编译器,但突然出现了这个警告。
      • @Evan Teran:啊,谢谢,我不知道编译器,并认为它是一个“全局”标志/开关/属性。但是当它是一个函数属性并且只有一些有它们时,我明白了。
      • Here's the documentation 在 GCC 的 warn_unused_result 函数属性上。如果您使用 GCC(Ideone 使用)并查看 stdio.h 头文件,您会看到 scanf 及其亲属被标记为 warn_unused_result 属性,但 printf 及其亲属被标记为不是。
      【解决方案7】:

      我用 gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3 尝试了你的例子。 当且仅当优化时发出警告,例如,使用选项 -O2 或 -O3。 请求所有警告(-Wall)并不重要。 转换为 void 的经典习语没有效果,它不会抑制警告。

      我可以通过写来消除警告

      if(scanf("%d",&t)){};
      

      这行得通,但对我来说有点晦涩难懂。 Empty {} 避免了另一个警告 -Wempty-body

      【讨论】:

      • 这可能会导致编译器发出额外的空正文警告
      • 如果我看到那种代码,if 末尾有一个分号,我会认为这可能是一个错误。它至少需要一个解释性评论。
      • 如此不负责任的牛仔建议获得了如此多的支持??颤抖
      • Mawg 是对的,你应该经常检查 scanf 的返回值。我一直在寻找一种让它静音的方法,更多的是作为一项运动,而不是作为良好编码的建议。我仍然觉得奇怪的是 (a) 优化会影响您是否收到警告,而 (b) -Wall 不会。
      【解决方案8】:

      警告(正确)表明不检查scanf 的返回值是个坏主意。函数 scanf 已显式声明(通过 gcc 函数属性),如果您丢弃其返回值,则会触发此警告。

      如果你真的想忘记这个返回值,同时保持编译器(和你的良心)高兴,你可以将返回值强制转换为 void:

      (void)scanf("%d",&t);
      

      【讨论】:

      • 即使您这样做,GCC 仍会发出警告。有关更多信息,请参阅此讨论gcc.gnu.org/bugzilla/show_bug.cgi?id=25509
      • 如果您分配给一个虚拟变量,那么您可能会收到另一个警告,关于该变量已分配但从未使用过。
      • 警告:许多编译器会优化掉这个 void cast,因为它是不必要的,然后仍然会产生一个未使用的变量错误。
      • @AffluentOwl void 演员表是标准约定,表示“此结果是故意未使用的”并且被普遍认可——除了 gcc,因为有人(Stallmann?)在 20 年前决定不应该有忽略warn_unused_result的方法。
      • (void)! ... 可用于至少避免gcc 警告(@kraffenetti @AffluentOwl),请参阅gcc.gnu.org/bugzilla/show_bug.cgi?id=66425#c34
      【解决方案9】:

      这样做:

      int main() {
          int t;
          int unused __attribute__((unused));
          unused = scanf("%d",&t);
          printf("%d",t);
          return 0;
      }
      

      【讨论】:

      • 遗憾的是,这看起来不太便携。
      • 这是对可能的错误的回避;您应该检查 scanf 的返回值是否符合您的预期。 (在这种情况下应该是 1)
      【解决方案10】:

      scanf, printf 是返回值的函数,通常在这类函数中是读取或写入的字符数。如果发生错误,您也可以使用返回码捕获错误。 一个好的编程习惯是查看返回值,但是,我从未见过有人查看 printf 返回值...

      如果您希望警告消失,您可以更改编译器的严重性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-02
        • 2021-10-03
        • 2015-04-06
        相关资源
        最近更新 更多