考虑
void swap(int* a, int* b)
{
if (a != b){
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
}
int main()
{
int a = 0;
int b = 1;
swap(&a, &b); // after this b is 0 and a is 1
return a > b ? 0 : a / b;
}
swap
试图欺骗编译器不优化程序。
是否定义了此程序的行为?永远无法访问a / b
,但如果是,那么您可以将除以零。
答案 0 :(得分:26)
没有必要在任何给定的代码构造或实践的有用性上建立这个问题的立场,也不必在任何关于C ++的内容上,无论是在其标准中还是在另一个SO答案中,无论C ++和C ++如何相似#39;定义可能是。要考虑的关键是C's definition of undefined behavior:
行为,使用的非便携或错误的程序构造或 错误的数据,本国际标准没有规定 要求
(C2011,3.4.3 / 1;重点补充)
因此,未定义的行为是暂时触发的("在使用"构造或数据时),而不仅仅是存在。 * 这对于未定义的行为是一致的很方便数据和程序结构产生的数据;标准不需要在那里保持一致。正如另一个答案所描述的,这个"在使用时"定义是一个很好的设计选择,因为它允许程序避免执行与错误数据相关的未定义行为。
另一方面,如果程序 执行未定义的行为,那么从标准的定义开始,程序的整个行为是未定义的。由于直接与错误数据或构造相关联的UB原则上可以包括改变程序的其他部分的行为,甚至追溯(或显然是这样),因此这种随之而来的不确定性是更普遍的类型。对于可能发生的事情当然有语言上的限制 - 所以不,鼻子恶魔实际上不会出现任何外表 - 但这些并不一定像人们想象的那样强烈。
* 警告:某些程序构造在翻译时使用。它们在程序转换中产生UB,结果是程序的每次执行都具有完全未定义的行为。对于一个有点愚蠢的例子,如果你的程序源没有以未转义的换行结束,那么程序的行为是完全未定义的(参见C2011, 5.1.1.2/1,第2点)。
答案 1 :(得分:22)
未评估的表达式的行为与程序的行为无关。如果表达式被评估,则未定义的行为与程序的行为无关。
如果确实如此,那么这段代码就没用了:
if (p != NULL)
…; // Use pointer p.
(你的XOR可能有未定义的行为,因为它们可能产生一个陷阱表示。你可以通过声明一个对象是易失性来打败像这样的学术例子的优化。如果一个对象是易失性的,C实现不能知道它的值由于外部手段可能会发生变化,因此每次使用对象都需要实现读取其值。)
答案 2 :(得分:0)
通常,如果未执行,将在执行时调用未定义行为的代码不会产生任何影响。但是,在某些情况下,实际实现可能会以相反的方式运行,并拒绝生成代码,这些代码虽然不是约束违规,但却无法在定义的行为中执行。
extern struct foo z;
int main(int argc, char **argv)
{
if (argc > 2) z;
return 0;
}
通过阅读标准,它明确地将不完整类型的左值转换描述为调用未定义的行为(除此之外,还不清楚实现可以为这样的事物生成代码),因此标准不会对行为施加任何要求如果argc
为3或更多。我无法在标准中找出上述代码违反的任何约束,但是,如果argc
为2或更小,也不应完全定义任何理由行为。尽管如此,包括gcc和clang在内的许多编译器完全拒绝了上述代码。