为什么C ++下溢/溢出行为被认为是未定义的?

时间:2014-11-16 12:05:48

标签: c++

我知道整数下溢和溢出是未定义的。

但是,考虑到C ++最终编译为汇编,实际上是不是定义了行为?

按位表示保持不变,整数格式保持不变0111..11将始终翻转到1000..00,对于下溢相同,那么为什么不将其视为已定义的行为?

关于汇编汇编,我是从我们在学校教过的基本程序集中得出的,但代码块给出了

int x = INT_MAX;
int y = x+1;

编译到

00401326    movl   $0x7fffffff,0x8(%esp)
0040132E    mov    0x8(%esp),%eax
00401332    inc    %eax
00401333    mov    %eax,0xc(%esp)

现在,无论x的值如何,总是会有inc或add指令?那么,未定义的行为出现在哪里?

3 个答案:

答案 0 :(得分:6)

  

但是,鉴于C ++最终编译为汇编,实际定义的行为是不是?

不,因为编译器决定它发出什么样的程序集。如果编译器希望,它可以生成在遇到未定义行为时擦除硬盘的程序集。

(实际上,“C ++最终编译成汇编”可能不是真的。存在C++ interpreters, for example - 标准没有规定C ++应该如何编译/编译。

标准的创建者决定不定义的原因之一是 - 几乎总是 - 优化的机会。如果有符号溢出是UB,那么编译器可以假设x + 1 > x始终为真,并生成依赖于此前提条件的更简单/更短/更快的代码。

答案 1 :(得分:5)

在C ++标准中未定义有符号整数的溢出,正是因为不同的编译器,汇编器和平台可能会以不同的方式解释它们。

当你知道一个程序会运行时,你可以推断一个程序的行为,但是如果没有这方面的知识就不可能预测它的行为方式。

  

按位表示保持不变,整数格式保持不变

这根本不是真的。

答案 2 :(得分:0)

IIRC,这是未定义的原因是因为C ++并没有强制要求目标机器如何存储数字。

假设每字节8位/ char。这会给我们:

  • std::numeric_limits<char>::max()
    • 2&#39;补充:127(0b01111111)
    • 1&#39;补充:127(0b01111111)
    • 签名幅度:127(0b01111111)
  • std::numeric_limits<char>::min()
    • 2&#39;补充:-128(0b10000000)
    • 1&#39;补充:-127(0b10000000)
    • 签名幅度:-127(0b11111111)

您可以看到最小值,即我们有不同的位模式和最小值,而最大值是相同的。

那么,如果你最多加1会怎么样?让我们假设我们转为无符号,加1,转回签名。结果将是:

  • 2&#39;补充:-128(0b10000000)
  • 1&#39;补充:-127(0b10000000)
  • 符号幅度:-0(0b10000000)

相当混乱。但是,如果我们想要明确定义溢出,我们能做什么?我们假设我们有一个signed char c = 127;并且想要添加1.我们可以定义结果应该始终为-127,因为所有三个引用的系统都可以表示(忽略这些不是&# 39; t唯一表示有符号整数的系统)。但这意味着编译器必须专门捕获溢出并在2的补码(大多数系统)和签名的幅度系统上正确处理它,这意味着额外的指令,因此在那些机器上的性能不太理想。

你不太可能遇到一台在现实生活中没有使用2的补充机器,所以C ++人员不能简单地强制要求吗?我还没有发现任何当前的 CPU或DSP使用除2的补充以外的任何东西,但是当C ++创建时, 机器使用1&# 39; s补码(例如CDC Cyber),听到有些DSP今天仍然存在(毕竟有DSP有char sizes other than 8 bit),我不会感到惊讶。这就是为什么它保持未定义的行为。