有许多声称使用未初始化的变量会调用undefined behavior (UB)
仔细阅读文档,我无法验证这种说法,所以我想要一个令人信服的论点,为C和C ++澄清这一点。
我希望两者都有相同的语义,但我准备对微妙或不那么微妙的差异感到惊讶。
使用未初始化变量开始的一些示例。请根据需要添加其他人,以解释他们不会涵盖的任何角落案例。
void test1() {
int x;
printf("%d", x);
}
void test2() {
int x;
for(int i = 0; i < CHAR_BIT * sizeof x)
x = x << 1;
printf("%d", x);
}
void test3() {
unsigned x;
printf("%u", x); /* was format "%d" */
}
void test4() {
unsigned x;
for(int i = 0; i < CHAR_BIT * sizeof x)
x = x << 1;
printf("%u", x); /* was format "%d" */
}
答案 0 :(得分:9)
在C中,所有这些都是未定义的行为,但出于一个可能不会直接考虑的原因。访问具有不确定值的对象具有未定义的行为(如果是) &#34;无记忆&#34;那是6.3.2.1 p2
如果左值指定一个自动存储持续时间的对象 可以用寄存器存储类声明(从来没有 它的地址),并且该对象未初始化(未声明 使用初始化程序并且之前未执行任何赋值 使用),行为未定义。
否则,如果采取了地址,那么在这种情况下对具体的不确定性的解释并不是一致的。有些人一旦首次阅读就会期望这样的价值得到修复,其他人会说像&#34; woobly&#34; (或左右)每次访问时可能不同的值。
总之,不要这样做。 (但你可能已经知道了。)
(而不是使用&#34;%d&#34;对于unsigned
来说错误。)
答案 1 :(得分:8)
<强> C 强>
C11 6.7.9 / 10
如果未初始化具有自动存储持续时间的对象 显然,它的价值是不确定的。
不确定值的处理如下:
C11 6.2.6.1/5
某些对象表示不需要表示的值 对象类型。如果对象的存储值具有这样的值 表示,并由没有的左值表达式读取 字符类型,行为未定义。如果这样的表示是 由副作用产生,修改对象的全部或任何部分 通过一个没有字符类型的左值表达式, 行为未定义50)。这种表示称为陷阱 表示。
对上述规范性文本有评论:
50)因此,可以将自动变量初始化为陷阱 表示没有导致未定义的行为,但是值 在存储适当的值之前,不能使用该变量。
(强调我的)
此外,左移一个包含不确定值的signed int变量也会导致未定义的行为,以防它被解释为否定行为:
C11 6.5.7 / 4
E1的结果&lt;&lt; E2是E1左移E2位位置;腾空 位用零填充。如果E1具有无符号类型,则值为 结果是E1×2E2,比最大值减少了一个模数 在结果类型中可表示。如果E1有签名类型和 非负值,E1×2E2可在结果类型中表示, 那就是结果价值;否则,行为是 未定义。
答案 2 :(得分:4)
所有四种情况都在C中调用未定义的行为,因为未初始化的自动变量从未获取其地址。看到不同的答案。
顺便说一下,sizeof(x)
是定义的,因为表达式实际上没有被评估:它是一个衰减到类型的编译时评估。
在最新的C ++ 1y草案(N3936)中,这显然是the language on indeterminate values and undefined behavior has been clarified以来未定义的行为,现在在8.5
部分中说明了:
[...]如果评估产生了不确定的值,则行为是不确定的,除非在下列情况下;
继续列出一些无符号窄字符类型的异常。
以前在C ++中,我们不得不依赖underspecified lvalue-to-rvalue conversion来证明未定义的行为,这在一般情况下是有问题的。在这种情况下,我们确实有一个lalue-to-rvalue转换。如果我们查看5.2.2
函数调用段 7 ,其中写着(强调我的):
当给定参数没有参数时,参数的传递方式使得接收函数可以通过调用va_arg(18.10)来获取参数的值。 [...] 对参数执行左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)标准转换表达强>
答案 3 :(得分:3)
关于C,所有示例的行为可能未定义:
3.19.2
1 不确定值
要么是未指定的值,要么是陷阱表示
...
6.2.6类型表示
6.2.6.1概述
...
5某些对象表示不需要表示对象类型的值。如果存储 对象的值具有这样的表示,并由左值表达式读取 没有字符类型,行为是未定义的。如果产生这样的表示 通过副作用,通过左值表达来修改对象的全部或任何部分 没有字符类型,行为是未定义的。 50)这样的表示被称为 陷阱表示。
...
50)因此,可以将自动变量初始化为陷阱表示而不会导致未定义 行为,但在存储适当的值之前,不能使用变量的值。
在所有四种情况下,x
都有自动存储持续时间,并且没有明确初始化,这意味着它的值是不确定的;如果此不确定值是陷阱表示,则行为未定义。
编辑
删除了对附录J的引用,因为它是非规范性的。