我正在阅读有关未定义行为的内容,我不确定它是否只是一个编译时功能,或者它是否可以在执行时发生。
我很好理解这个例子(这是从Undefined Behavior page of Wikipedia中提取的):
C语言的一个例子:
int foo(unsigned x) { int value = 5; value += x; if (value < 5) bar(); return value; }
x
的值不能为负,并且鉴于有符号整数溢出在C中是未定义的行为,编译器可以假设在if检查value >= 5
的行。因此if
和函数bar
的调用可以被编译器忽略,因为if
没有副作用,并且它的条件永远不会被满足。因此,上面的代码在语义上等同于:int foo(unsigned x) { int value = 5; value += x; return value; }
但这发生在编译时。
如果我写,例如:
void foo(int x) {
if (x + 150 < 5)
bar();
}
int main() {
int x;
std::cin >> x;
foo(x);
}
然后用户输入MAX_INT - 100
(“2147483547”,如果32位整数)。
会有一个整数溢出,但是AFAIK,它是CPU的算术逻辑单元会产生溢出,所以这里不涉及编译器。
它仍然是未定义的行为吗?
如果是,编译器如何检测溢出?
我能想到的最好的是CPU的溢出标志。如果是这种情况,是否意味着如果在执行时随时设置CPU的溢出标志,编译器可以执行他想要的任何操作?
是的,但不一定是我认为你可能意味着它的方式,也就是说,如果在机器代码中有一个添加,并且在运行时添加包装(或以其他方式溢出,但在大多数架构上它将包装)不是UB通过它自己。 UB完全属于C(或C ++)领域。这个添加可能是添加无符号整数或者是编译器可以进行的某种优化,因为它知道目标平台的语义并且可以安全地使用依赖包装的优化(但你不能,除非你当然用无符号做类型)。
当然,这根本不意味着使用“仅在运行时换行”的构造是安全的,因为这些代码路径在编译时也会中毒。例如,在您的示例中,
extern void bar(void);
void foo(int x) {
if (x + 150 < 5)
bar();
}
由GCC 6.3编译,目标是x64
foo:
cmp edi, -145
jl .L4
ret
.L4:
jmp bar
这相当于
void foo(int x) {
if (x < -145)
bar(); // with tail call optimization
}
..如果你假设有符号整数溢出是不可能的,那就是相同的(在某种意义上它会在输入上放置一个隐含的前提条件,使溢出不会发生)。