在执行时上溢/下溢是否为未定义的行为?

问题描述 投票:5回答:1

我正在阅读有关未定义行为的内容,我不确定它是否只是一个编译时功能,或者它是否可以在执行时发生。

我很好理解这个例子(这是从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的溢出标志,编译器可以执行他想要的任何操作?

c++ c undefined-behavior integer-overflow
1个回答
6
投票

是的,但不一定是我认为你可能意味着它的方式,也就是说,如果在机器代码中有一个添加,并且在运行时添加包装(或以其他方式溢出,但在大多数架构上它将包装)不是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
}

..如果你假设有符号整数溢出是不可能的,那就是相同的(在某种意义上它会在输入上放置一个隐含的前提条件,使溢出不会发生)。

© www.soinside.com 2019 - 2024. All rights reserved.