读取未确定值是否属于未定义行为?

11
问题是在回答问题“转换为int时,C/C++的bool类型是否总是保证为0或1?”的评论中出现的。
涉及的代码分配了一个(本地)未初始化值的bool数组。
const int n = 100;
bool b[n];

很明显,b 中的值是不确定的。

一些评论者认为,例如读取 b[0] 是未定义行为。C++ 标准中是否有这样的规定?我仍然坚信相反:

  1. 显然已分配存储并完成了基本布尔类型的初始化,因为它没有构造函数。因此,它肯定不同于解引用未初始化指针,或调用未初始化的非平凡对象的方法/转换运算符。标准似乎涵盖了这些特定情况。

  2. 在 C 中,行为确实是未定义的:What happens to a declared, uninitialized variable in C? Does it have a value?,一些答复者似乎混淆了两者。

  3. 在我找到的最新的 C++0x 草案中,我找不到任何关于不确定值的定义,也没有任何定义允许访问此类值触发处理器陷阱。事实上,Bjarne Stroustrup 不确定不确定值可能是什么:http://zamanbakshifirst.blogspot.com/2007/02/c-indeterminate-value.html


1
哦,"未定义行为"是StackOverflow这里非常喜欢的一个术语,它被用于从实现定义、未指定到真正的未定义的所有情况。 - Šimon Tóth
1
我的意思是那种传说中的“鼻涕鬼”式的未定义。 - Sebastian
顺便提一下,8.5-9讲到了这个问题。 - Šimon Tóth
2
@Let_Me_Be:这可能是真的,有时人们可能会错误地将某些情况分类,即使他们理解差异。但这是UB在唯一重要的意义上,也就是标准中定义的意义。 - Steve Jessop
请注意,我对于“未初始化的局部变量是否是最快的随机数生成器?”这个问题的回答也是相关的。我们可以看到编译器正在积极利用不确定值周围的未定义行为。 - Shafik Yaghmour
4个回答

7
是的,正式地说,未确定值的rvalue转换是UB(除了unsigned char,原本我写了“和变体”,但据我回忆,正式版本适用于1的补码有符号字符,其中可能使用负0作为陷阱值)。
我太懒了,不想为你查找标准段落,也懒得关心因此而获得的踩数。
然而,在实践中,只有在(1)古老的架构上,以及(2)64位系统上才会出现问题。
编辑:哎呀,我现在似乎记起来有一个博客文章和相关的缺陷报告,涉及访问未确定的char的正式UB。所以也许我必须实际检查标准,+搜索DRs。啊,那就等会儿吧,现在喝咖啡!
编辑2:Johannes Schaub很友好地提供了这个 SO问题的链接,其中讨论了访问char的UB。所以,我就从那里记起来了!谢谢,Johannes。
干杯& hth.,

@downvoter:请说明下投反对票的原因,这样其他人就可以了解回答有什么问题,或者为什么你的反对票是无意义的。 - Cheers and hth. - Alf
5
非函数、非数组类型 T 的左值(lvalue)可以转换为右值(rvalue)。如果 T 是不完整类型,则需要进行此转换的程序是非法的。如果该左值所指向的对象既不是类型为 T 的对象,也不是类型派生自 T 的对象,或者该对象未初始化,则需要进行此转换的程序具有未定义行为。 - Steve Jessop
1
嘿,我不会因为你的懒惰而给你投反对票。然而,我也可能不会因此给你点赞。 - default
你的链接指向youtube.com,而不是一个SO问题。这很有趣,但可能不是你想要的 :-) - Sebastian
@Sebastian:抱歉,我无法帮助你解决C++0x的问题,因为我还不理解那些额外的值类型。我只是在谈论标准。我认为这很不可能故意在C++0x中定义行为,因为我所能想到的唯一定义的行为是说bool有一个值(true或false),但是它的值是未指定的。实际上,这意味着每当从较大的存储器读取bool值时都需要进行掩码操作,而没有任何好处。 - Steve Jessop
显示剩余2条评论

7
这个问题的答案随着最新的C++1y工作草案(N3946)而改变,我们可以在这里找到。第8.5节“初始化器”第12段与C++03和C++11有很大不同,现在包含以下内容(重点是我的):
如果未为对象指定初始化程序,则会默认初始化该对象。当获得具有自动或动态存储期限的对象的存储空间时,该对象具有不确定值,并且如果未对该对象执行初始化,则该对象将保留不确定值,直到该值被替换(5.17)。[注意:具有静态或线程存储期限的对象将被零初始化,请参见3.6.2。-结束注释]如果评估产生不确定值,则行为除以下情况外都是未定义的:

并列出了一些仅适用于无符号窄字符类型的异常情况,我在 C++11相对于使用不确定值和未定义行为方面发生了变化吗?中有一个完整的引用。

因此,在您的情况下,b具有自动存储期,并且未初始化,因此具有不确定的值。因此,评估b [0]确实是未定义的行为。

以前,我们需要使用左值到右值转换来证明这是未定义的,但这是有问题的,因为转换是未指定的

请注意,本节中的不确定值是用斜体字表示的,因此它意味着正在现场定义该术语。因此,现在C ++ 1y实际上定义了该术语。以前该术语被使用而没有定义,这在缺陷报告616中有所涵盖。


5

3.9.1基本类型下,关于bool,标准规定:

bool类型的值只能是true或false。

注释中说明:

在国际标准中,如果使用bool值的方式被描述为“未定义”,例如检查未初始化的自动对象的值,可能会导致它的行为既不是真也不是假。


1
读取未确定的值通常会导致未定义行为,这不仅仅是一个"理论"问题。即使对于所有可能的位模式都有定义值的类型,也不应该认为未确定的值以与未指定的值不同的方式表现是"令人惊讶"的。例如,如果*p保持未确定的值,并且x除了所示之外没有在任何地方使用,那么代码:
uint32_t x,y,z;
...
x = *p;
if (condition1) y=x;
... code that "shouldn't" affect *p if its value is defined
if (condition2) z=x;

could be rewritten as:

if (condition1) y=*p;
... code that "shouldn't" affect *p if its value is defined
if (condition2) z=*p;

如果*p的值为未确定值,则编译器不会禁止在两个“if”语句之间的代码修改其值。例如,如果*p占用的存储空间在释放和重新分配之前被“float”占用,编译器可能会在上述两个“if”语句之间写入该“float”值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接