是否定义了在无法到达的路径上具有未定义行为的程序的行为?

考虑

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永远不可达,但如果是,那么你会得到除零。

无论C ++的定义有多么相似,无论是在标准中还是在另一个SO答案中,都没有必要在任何给定的代码构造或实践的有用性上,或在任何关于C ++的任何内容的基础上建立立场。 要考虑的关键是C对未定义行为定义 :

行为, 在使用不可移植或错误的程序结构或错误数据时,本国际标准不对此要求

(C2011,3.4.3 / 1;重点补充)

因此,未定义的行为在时间上触发(“在使用时”构造或数据),而不仅仅是存在。 *这对于数据和程序结构产生的未定义行为是一致的,这是很方便的; 标准不需要在那里保持一致。 正如另一个答案所描述的那样,这种“使用后”定义是一个很好的设计选择,因为它允许程序避免执行与错误数据相关的未定义行为。

另一方面,如果程序确实执行了未定义的行为,那么从标准的定义开始,程序的整个行为是未定义的。 由于直接与错误数据或构造相关联的UB原则上可以包括改变程序的其他部分的行为,甚至追溯(或显然是这样),因此这种随之而来的不确定性是更一般的类型。 对于可能发生的事情当然有语言上的限制 – 所以不,鼻子恶魔实际上不会出现任何外表 – 但这些并不一定像人们想象的那样强烈。


*警告:一些程序构造在翻译时使用。 它们在程序转换中产生UB,结果是程序的每次执行都具有完全未定义的行为。 对于一个有点愚蠢的例子,如果你的程序源没有以未转义的换行结束,那么程序的行为是完全未定义的(参见C2011,5.1.1.2 / 1 ,第2点)。

未评估的表达式的行为与程序的行为无关。 如果表达式被评估,则未定义的行为与程序的行为无关。

如果确实如此,那么这段代码将毫无用处:

 if (p != NULL) …; // Use pointer p. 

(你的XOR可能有未定义的行为,因为它们可能产生一个陷阱表示。你可以通过声明一个对象是易失的来破坏这样的学术例子的优化。如果一个对象是易失性的,C实现不能知道它的值是否可能改变到期到外部意味着,所以每次使用对象都需要实现来读取它的值。)

通常,如果执行未调用行为,则在执行时不会产生任何影响。 然而,在一些情况下,实际实现可能以相反的方式表现并且拒绝生成代码,该代码虽然不是约束违反,但是不可能在定义的行为中执行。

 extern struct foo z; int main(int argc, char **argv) { if (argc > 2) z; return 0; } 

通过我对标准的阅读,它明确地将不完全类型的左值转换描述为调用未定义的行为(除此之外,还不清楚实现可以为这样的事物生成代码),因此如果argc是,则标准不会对行为施加任何要求。 3个或更多。 我无法在标准中找出上述代码违反的任何约束,但是,如果argc为2或更小,也不应完全定义任何理由行为。 尽管如此,包括gcc和clang在内的许多编译器完全拒绝上述代码。