如果a没有被初始化,是a a还是aa未定义的行为?
考虑这个程序:
#include <stdio.h> int main(void) { unsigned int a; printf("%u %u\n", a^a, aa); return 0; }
它是不确定的行为?
从表面上看, a
是一个未初始化的variables。 所以这指向未定义的行为。 但是a^a
和aa
对于aa
的所有值都等于0
,至less我认为是这样的。 有没有可能有某种方式来争辩这种行为是有明确定义的?
在C11:
- 根据6.3.2.1 / 2的规定,如果从不具有地址(如下所示)
- 它可能是一个陷阱表示(当访问时导致UB)。 6.2.6.1/5:
某些对象表示不需要表示对象types的值。
无符号整数可以具有陷阱表示(例如,如果它具有15个精度位和1个奇偶校验位,则访问a
会导致奇偶校验错误)。
6.2.4 / 6说初始值是不确定的,3.19.2下的定义是一个未指定的值或一个陷阱表示 。
更进一步:在C11 6.3.2.1/2中,Pascal Cuoq指出:
如果左值指定一个可以用寄存器存储类声明的自动存储持续时间的对象(从来没有取得地址),并且该对象是未初始化的(没有用初始化器声明,并且在使用之前没有对它进行赋值),行为是不确定的。
这个字符types没有例外,所以这个子句似乎取代了前面的讨论。 即使不存在陷阱表示,访问x
也立即未定义。 此子句已添加到C11以支持实际上具有寄存器陷阱状态的安腾CPU。
没有陷阱表示的系统:但是如果我们抛出&x;
那么6.3.2.1/2的异议就不再适用了,而且我们所在的系统已知没有陷阱表示? 那么这个值是一个未指定的值 。 3.19.3中对未指定值的定义有些模糊,但是DR 451对此做了澄清,得出的结论是:
- 在所描述的条件下未初始化的值可能会改变其值。
- 任何对不确定值执行的操作都会有一个不确定的值。
- 使用不确定值时,库函数将显示未定义的行为。
- 这些答案适用于所有没有陷阱表示的types。
在这个决议下, int a; &a; int b = a - a;
int a; &a; int b = a - a;
导致b
仍然有不确定的价值。
请注意,如果不确定的值不传递给库函数,我们仍然处于未指定的行为(不是未定义的行为)领域。 结果可能很奇怪,例如if ( j != j ) foo();
可以叫foo,但恶魔必须保持在鼻腔内。
是的,这是未定义的行为。
首先,任何未初始化的variables都可能具有“破碎”(又名“陷阱”)表示。 即使是单次尝试访问该表示也会触发未定义的行为。 而且,即使是非陷印types的对象(如unsigned char
)仍然可以获得特殊的平台依赖状态(如Itanium上的NaT – Not-A-Thing),这可能performance为“不确定值”的performanceforms。
其次,一个未初始化的variables不能保证有一个稳定的值。 对相同的未初始化的variables的两个顺序访问可以读取完全不同的值,这就是为什么即使两个访问都是“成功”(而不是陷阱),仍然不能保证a - a
将评估为零。
如果一个对象具有自动存储持续时间,并且不采取地址,尝试读取它将产生未定义的行为。 取这样一个对象的地址并使用“unsigned char”types的指针来读出它的字节,标准保证产生一个types为“unsigned char”的值,但并不是所有的编译器都遵守这个标准。 例如ARM GCC 5.1时,给出:
#include <stdint.h> #include <string.h> struct q { uint16_t x,y; }; volatile uint16_t zz; int32_t foo(uint32_t x, uint32_t y) { struct q temp1,temp2; temp1.x = 3; if (y & 1) temp1.y = zz; memmove(&temp2,&temp1,sizeof temp1); return temp2.y; }
将生成代码,如果y为零,则返回x,即使x在范围0-65535之外。 该标准明确指出,不确定值的无符号字符读取保证产生一个在unsigned char
范围内的值,并且memmove
的行为被定义为等同于一系列字符读写。 因此,temp2应该有一个可以通过字符写入顺序存储的值,但是gcc正在决定用一个赋值来代替memmove,而忽略代码取得temp1和temp2地址的事实。
有办法迫使编译器把一个variables看作是一个任意的types值,如果任何这样的值同样可以被接受的话,将是有帮助的,但是标准并没有规定一个干净的手段(保存用于存储一些可以工作的特定值,但通常是不必要的慢)。 即使是逻辑上强制variables来保存一个可以表示为一些位组合的值的操作也不能依赖于所有的编译器。 因此,这些variables没有什么用处。