上溢/下溢的确切行为仅针对unsigned
类型指定。
无符号整数应遵守算术模 2^n 的定律,其中 n 是该特定整数大小的值表示中的位数。
来源:草案 N3690 §3.9.1 第 4 句
这意味着无符号算术不会溢出,因为不能由得到的无符号整数类型表示的结果以比得到的无符号整数类型可以表示的最大值大一的数字为模减少。
资料来源:N3690 草案第 3.9.1 条注释 47
对于普通的有符号整数类型,C++ 标准只是简单地说,任何事情都可能发生。
如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义
资料来源:草案 N3690 §5 第 4 句
如果我们谈论的是 x86 处理器(或大多数其他现代处理器),那么行为确实正是您所描述的,对于 CPU,有符号值或无符号值之间没有区别(有有符号和无符号操作,但值本身只是位)。
请注意,编译器可以假设(并且大多数现代优化编译器实际上确实假设)在正确的程序中不会发生有符号整数溢出,例如在如下代码中:
int do_something();
int do_something_else();
void foo() {
int x = do_something();
int y = x + 1;
if (x < y) {
do_something();
} else {
do_something_else();
}
}
编译器可以完全跳过else
生成代码中的测试和分支,因为在有效程序中,带符号的 intx
始终小于x+1
(因为不能将带符号的溢出视为有效行为)。但是,如果您替换int
为unsigned int
,编译器必须为 test 和 else 分支生成代码,因为对于无符号类型,它可能是x > x+1
.
例如clang编译代码foo
为
foo(): # @foo()
push rax
call do_something()
pop rax
jmp do_something() # TAILCALL
您可以在其中看到 ode 只调用do_something
了两次(除了对 的奇怪处理)并且实际上rax
没有提及。do_something_else
或多或少相同的代码由gcc
.