【问题标题】:What's the behavior of an uninitialized variable used as its own initializer?用作自己的初始化程序的未初始化变量的行为是什么?
【发布时间】:2019-01-15 14:03:52
【问题描述】:

我刚才注意到下面的代码可以用clang/gcc/clang++/g++编译,使用c99c11c++11标准。

int main(void) {
    int i = i;
}

即使使用-Wall -Wextra,编译器也没有报告警告。

通过将代码修改为int i = i + 1;-Wall,他们可能会报告:

why.c:2:13: warning: variable 'i' is uninitialized when used within its own initialization [-Wuninitialized]
    int i = i + 1;
        ~   ^
1 warning generated.

我的问题:

  • 为什么编译器甚至允许这样做?
  • C/C++ 标准对此有何评论?具体来说,这是什么行为? UB 还是依赖于实现?

【问题讨论】:

标签: c++ c initialization language-lawyer


【解决方案1】:

因为i在用来初始化自身时是未初始化的,所以此时它有一个不确定值。不确定值可以是未指定值陷阱表示

If 你的实现支持整数类型的填充位,if 所讨论的不确定值恰好是陷阱表示,然后使用它会导致 未定义的行为

如果您的实现没有在整数中有填充,则该值只是未指定并且没有未定义的行为。

编辑:

更详细地说,如果i 从未在某个时候获取其地址,则该行为仍可能未定义。这在 C11 标准的第 6.3.2.1p2 节中有详细说明:

如果左值指定一个自动存储的对象 可以用寄存器存储声明的持续时间 类(从未获取过它的地址),并且该对象未初始化 (没有用初始化器声明,也没有分配给它 已在使用前执行),行为未定义。

因此,如果您从不使用i 的地址,那么您的行为未定义。否则,上述陈述适用。

【讨论】:

  • 包含标识符i 的范围从其声明符的末尾开始可能是相关的,初始化程序不是其中的一部分。因此i 在它自己的初始化器的作用域内,尽管用它自己初始化它是没有用的。
  • 另外,C11 6.3.2.1/2 周围还有一个问题,尽管我不认为这会导致您的分析不正确。
  • 你能详细说明一下吗,参考标准更好?
  • @JohnBollinger 这个答案基本上是正确的。可以在这里找到包括 6.3.2.1 特例在内的完整故事:(Why) is using an uninitialized variable undefined behavior?
  • @JohnBollinger 添加了有关 i 是否被占用地址的更多详细信息。
【解决方案2】:

这是一个警告,与标准无关。

警告是启发式的“乐观”方法。只有当编译器确定会出现问题时才会发出警告。在这种情况下,您最好使用clang 或最新版本的gcc,如 cmets 所述(请参阅我的另一个相关问题:why am I not getting an "used uninitialized" warning from gcc in this trivial example?)。

无论如何,在第一种情况下:

int i = i;

什么都不做,因为 i==i 已经。分配可能会被完全优化,因为它是无用的。对于不将自初始化视为问题的编译器,您可以在没有警告的情况下执行此操作:

int i = i;
printf("%d\n",i);

而这会触发警告:

int i;
printf("%d\n",i);

不过,不被警告已经够糟糕了,因为从现在开始i 被视为已初始化

第二种情况:

int i = i + 1;

必须执行未初始化值和1 之间的计算。那里发生未定义的行为。

【讨论】:

  • 好答案;但是,我不确定“当编译器确定会出现问题时发出的警告”。有很多警告,但情况并非如此,这在很大程度上取决于启用的警告类型
  • int i = i + 1 的风险在于 UB 是 UB,句号。此外,签名溢出。此外,当其他编码人员必须在稍后的某个时间点理解该代码时,很多人都会摸不着头脑。
  • 真的!我忘记了标志。编辑使其变得简单
  • 正如@Ctx 所说。例如,我经常发现自己构建的第三方代码会导致大量关于使用可能未初始化变量的警告。这些情况下的控制流分析通常表明,程序员包含了适当的逻辑,以确保在使用变量值之前为其分配一个值。但是编译器明确un确定是否有问题。
  • -Wparentheses 是另一个例子。
【解决方案3】:

我相信您可以在以下情况下收到警告

int i = i + 1; 

不过,正如预期的那样,您希望即使在以下情况下也会显示警告

int i = i;

还有。

为什么编译器甚至允许这样做?

这句话本身并没有错。查看相关讨论:

了解更多。

C/C++ 标准对此有何评论?具体来说,这是什么行为? UB 还是依赖于实现?

这是未定义的行为,因为int 类型可以具有陷阱表示,并且您从未在讨论中获取变量的地址。因此,从技术上讲,一旦您尝试使用存储在变量 i 中的(不确定的)值,您就会面临 UB。

您应该打开编译器警告。在gcc

【讨论】:

  • 这里没有-Winit-self 的骰子,使用gcc 7.3.1
  • 你有一个新版本的 gcc。我的比较老。当您说“未定义行为的可能来源(如果稍后使用变量的值)”时,它与未初始化使用它相同。
猜你喜欢
  • 1970-01-01
  • 2020-04-13
  • 2021-11-11
  • 2019-06-18
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多