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

Bat*_*eba 24 c undefined-behavior language-lawyer

考虑

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;
}
Run Code Online (Sandbox Code Playgroud)

swap 试图欺骗编译器不优化程序.

是否定义了此程序的行为?a / b是永远无法达到的,但如果是,那么你会得到除零.

Joh*_*ger 26

无论C++的定义有多么相似,无论是在标准中还是在另一个SO答案中,都没有必要根据任何给定的代码构造或实践的有用性,以及任何关于C++的任何内容来定位这个问题.要考虑的关键是C对未定义行为定义:

行为,在使用非便携式的或错误的程序构建体的或错误的数据,其中该国际标准规定的要求没有

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

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

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


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


Eri*_*hil 22

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

如果确实如此,那么这段代码就没用了:

if (p != NULL)
    …; // Use pointer p.
Run Code Online (Sandbox Code Playgroud)

(您的XOR可能具有未定义的行为,因为它们可能会产生陷阱表示.您可以通过声明对象是易失性来破坏此类学术示例的优化.如果对象是易失性的,则C实现无法知道其值是否可能更改到期到外部意味着,所以每次使用对象都需要实现来读取它的值.)

  • 请注意,如果可以证明最终会达到UB,那么UB的表达式不一定非必要导致UB. (5认同)
  • @EugeneSh.我的意思是一个程序有UB,如果可以证明路径在所有条件下都会导致UB表达式.也就是说,如果在给定点可以证明所有路径都通向UB,则该程序在该点已经是UB.编译器可以做出该确定并相应地动作,在达到实际UB表达式之前改变行为. (3认同)