我刚才注意到,可以使用c99
,c11
,c++11
标准用clang / gcc / clang ++ / g ++编译以下代码。
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.
我的问题:
答案 0 :(得分:10)
由于i
在用于初始化自身时未初始化,因此当时具有不确定值。不确定值可以是未指定值或陷阱表示。
如果您的实现支持整数类型的填充位,而 if 所讨论的不确定值恰好是陷阱表示,则使用它会导致不确定的行为。
如果您的实现没有具有整数填充,则该值只是未指定,并且没有没有未定义的行为。
编辑:
为进一步详细说明,如果i
从未在某个时间获取其地址,则该行为仍然无法定义。 C11标准的6.3.2.1p2部分对此进行了详细说明:
如果左值指定自动存储的对象 寄存器存储可能已声明的持续时间 类(从未使用过其地址),并且该对象未初始化 (未使用初始化程序声明且未对其进行赋值 已经在使用前执行过),则行为未定义。
因此,如果您从不使用i
的地址,那么您将有未定义的行为。否则,以上声明适用。
答案 1 :(得分:9)
这是警告,与标准无关。
通过“乐观”方法对警告进行启发。仅当编译器确定将要出问题时才发出警告。在这种情况下,您最好使用clang
或gcc
的最新版本,如评论中所述(请参阅我的另一个相关问题: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
之间进行计算。发生未定义的行为。
答案 2 :(得分:2)
我相信您可以在出现以下情况时得到警告
int i = i + 1;
按预期,但是,即使出现以下情况,您仍希望显示警告:
int i = i;
也。
为什么编译器甚至允许这样做?
该语句没有天生的错误。请参阅相关讨论:
获得更多见识。
C / C ++标准对此有何看法?具体来说,这是什么行为? UB还是依赖于实现?
这是未定义的行为,因为类型int
可以具有陷阱表示,并且您从未在讨论中使用过变量的地址。因此,从技术上讲,一旦尝试使用存储在变量i
中的(不确定的)值,您将面临UB。
您应该打开编译器警告。在gcc
中,
-Winit-self
to get a warning. in C。-Winit-self
已通过-Wall
启用。