【问题标题】:Why gcc and clang both don't emit any warning?为什么 gcc 和 clang 都没有发出任何警告?
【发布时间】:2017-07-27 09:55:17
【问题描述】:

假设我们有这样的代码:

int check(){
    int x = 5;

    ++x; /* line 1.*/

    return 0;
}

int main(){
    return check();
}

如果 line 1 被注释掉并且编译器在启用所有警告的情况下启动,它会发出:

warning: unused variable ‘x’ [-Wunused-variable]

但是,如果我们取消注释 line 1,即增加 x,则不会发出警告。

这是为什么呢?增加变量并没有真正使用它。

在 GCC 和 Clang 中都会发生这种情况。

【问题讨论】:

  • 这听起来更像是一个定义使用变量的含义。在我的世界中,语句中包含变量意味着我正在使用该变量。
  • 试试-O3 ..
  • 被“使用”与“有用”无关:)
  • 实际上,如果编译器真的知道某个变量是否未被使用,那会很烦人。有时你需要一个看起来没有被使用的变量,一个常见的技巧是写(void)x来隐藏警告。如果编译器将其识别为 x 未被使用,则无法消除警告
  • 如果x 不是原始类型并且它的类型会重载operator ++ 的含义怎么办?

标签: c c++ c++ c gcc clang compiler-warnings


【解决方案1】:

是的。

x++x = x+1; 相同,赋值。当您分配某物时,您可能无法跳过 使用它。结果没有被丢弃。

另外,来自online gcc manual,关于-Wunused-variable 选项

当局部或静态变量在声明之外未使用时发出警告。

因此,当您评论x++; 时,它满足生成并发出警告消息的条件。当您取消注释时,编译器可以看到该用法(此特定“用法”的“有用性”值得怀疑,但它仍然是一种用法)并且没有警告。

【讨论】:

    【解决方案2】:

    使用预增量,您将增加并再次分配值给变量。就像:

    x=x+1
    

    正如 gcc 文档所说:

    -Wunused-变量: 每当局部或静态变量在声明之外未使用时发出警告。

    如果您注释该行,则表示您没有使用声明它的行之外的变量

    【讨论】:

    • 我是 OP,这是一个自赋值,这个值是一个本地(或自动)变量,所以在这个例子中它不可能在这个范围之外使用。所以我觉得奇怪的是编译器会停止发出警告。
    • @NadavL 正如 gcc 文档所说:当局部或静态变量在声明之外未使用时发出警告。如果他评论第 1 行,则满足此条件并出现警告
    • 听起来很合法,谢谢!因此该变量仍未使用,但不是文档中定义的意义
    【解决方案3】:

    增加变量并没有真正使用它。

    确定这是使用它。它正在对存储的对象进行读取和写入访问。此操作对您的简单玩具代码没有任何影响,优化器可能会注意到这一点并完全删除该变量。但警告背后的逻辑要简单得多:警告 iff 该变量从未使用过。

    这实际上有一个好处,即您可以在有意义的情况下使该警告静音:

    void someCallback(void *data)
    {
        (void)data; // <- this "uses" data
    
        // [...] handler code that doesn't need data
    }
    

    【讨论】:

      【解决方案4】:

      这是为什么呢?增加变量并没有真正使用它。

      是的,它确实在使用它。至少从语言的角度来看。我希望优化器删除变量的所有痕迹。

      当然,该特定用途对程序的其余部分没有影响,因此该变量确实是多余的。我同意在这种情况下发出警告会有所帮助。但这不是你提到的关于未使用的警告的目的。

      但是,考虑到分析特定变量是否对程序的执行有任何影响通常是相当困难的。必须有一点,编译器会停止检查变量是否真的有用。似乎生成您测试的编译器警告的阶段仅检查变量是否至少使用一次。这曾经是自增操作。

      【讨论】:

        【解决方案5】:

        我认为对“使用”这个词以及编译器的含义存在误解。当您拥有++i 时,您不仅在访问变量,甚至还在修改它,AFAIK 这算作“使用”。

        对于编译器可以识别的“如何”变量的使用方式以及这些语句是否有意义是有限制的。事实上,clang 和 gcc 都会尝试删除不必要的语句,这取决于-O-flag(有时过于激进)。但是这些优化是在没有警告的情况下发生的。

        检测一个从未被访问或使用过的变量(没有进一步的声明提到该变量)相当容易。

        【讨论】:

          【解决方案6】:

          我同意你的观点,它可能会对此产生警告。我认为它不会产生警告,因为编译器的开发人员只是没有费心处理这种情况(还)。也许是因为它太复杂了。但也许他们将来会这样做(提示:你可以向他们提出这个警告)。

          编译器收到越来越多的警告。例如,GCC 中有-Wunused-but-set-variable (这是一个“新”警告,于 2011 年在 GCC 4.6 中引入),它对此发出警告:

          void fn() {
              int a;
              a = 2;
          }
          

          因此,期望它也会发出警告是完全可以的(这里没有什么不同,代码没有任何用处):

          void fn() {
              int a = 1;
              a++;
          }
          

          也许他们可以添加一个新警告,例如-Wmeaningless-variable

          【讨论】:

          • 这很棘手。有时可以完全优化归纳变量,但您只希望在变量与其他任何内容没有交互时发出警告。我想一个有趣的警告是当对死变量的最后一次操作是写/赋值时。因此,函数末尾的 (void)a 仍然会静音 a++ 的警告。
          【解决方案7】:

          根据 C 标准 ISO/IEC 9899:201x,始终执行表达式求值以允许产生表达式的副作用,除非编译器无法充分确定在程序执行时将其删除没有改变。

          5.1.2.3 程序执行

          在抽象机中,所有表达式都按照语义的规定进行评估。如果一个实际的实现可以推断出它的值没有被使用并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用),则它不需要评估表达式的一部分。

          删除线时

          ++x;
          

          编译器可以推断出局部变量x已定义并初始化,但未使用。

          当您添加它时,表达式本身可以被视为void 表达式,必须评估副作用,如下所述:

          6.8.3 表达式和空语句

          表达式语句中的表达式因其副作用而被评估为 void 表达式。

          另一方面,删除与未使用变量相关的编译器警告是很常见的,将表达式强制转换为 void。 IE。对于函数中未使用的参数,您可以编写:

          int MyFunc(int unused)
          {
             (void)unused;
             ...
             return a;
          }
          

          在这种情况下,我们有一个引用符号 unused 的 void 表达式。

          【讨论】:

          • 一个优化编译器仍然可以完全优化掉一个变量。请注意“需要”一词:如果一个实际的实现可以推断出它的值没有被使用并且没有产生需要的副作用,那么它就不需要评估表达式的一部分。 “。如果对此有警告,那就太好了,但-Wunused-variable 不是。不过,这个答案并不能证明这一点,因为++x 不是需要的一面-效果,因为 x 在那之后就死了(什么都没有读取)。
          • @PeterCordes 前/后递增/递减运算符是一种机构副作用运算符,因为它们在应用之前和之后产生创建 2 个值的副作用。即使x++; 可以写成x=x+1; 它根本不是等价的。对于规范语法第一个是null语句,只产生副作用(增量),第二个是赋值表达式。对于空语句,您应应用6.8.3 表达式和空语句中描述的内容。继续...
          • @PeterCordes 删除表达式x++; 声明(参见6.7 声明),初始化,int x=5; 独立是一个未使用的变量 .然后编译器告诉您可以使用开关-Wunused-variable 删除警告。如果它能够确定代码不流畅,抽象机器最终可以避免评估它的一部分,或者不管它。
          猜你喜欢
          • 2021-06-23
          • 2023-04-08
          • 1970-01-01
          • 2021-12-27
          • 1970-01-01
          • 2015-09-28
          • 1970-01-01
          • 2012-05-06
          相关资源
          最近更新 更多