C++下溢和上溢

6

你好,我是一名新手,请告诉我是否有任何错误,下一次我会尽力改进。

我正试图理解C++中的下溢和上溢。我的理解是如果变量的范围超出了其最大值或最小值,它将从范围的另一端开始。因此,如果short的最小值为-32768,并且我们对其进行-1操作,则新值应为SHRT_MAX。(32767) 以下是我的代码:

#include<iostream.h>
#include<limits.h>
#include<conio.h>
int main ( void )
{
 int testpositive =INT_MIN ;
 short testnegative = SHRT_MIN ; 
 cout<< SHRT_MIN<<"\n";
 cout << testnegative-1<<"\n";
 cout << INT_MIN << "\n";
 cout << testpositive-1 << "\n"; 
 cout<<testpositive-2;
 getch();
 return 0;
}   

1
操作数首先提升为int,因此没有溢出。考虑将结果强制转换回短整型以查看您所希望的内容。 - Hans Passant
太棒了。它能正常工作,现在我明白了。 - Knownow
考虑使用 #include <iostream> 而不是 #include <iostream.h> - L. F.
4个回答

12

只有对于无符号类型,溢出/下溢的确切行为才有规定。

无符号整数应遵守算术模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分支,因为在一个有效的程序中,有符号整数x总是小于x+1(因为有符号溢出不能被视为有效行为)。

然而,如果您将int替换为unsigned int,则编译器必须为测试和else分支生成代码,因为对于无符号类型,可能存在x > x + 1的情况。

例如,clang将foo的代码编译为

foo():                                # @foo()
        push    rax
        call    do_something()
        pop     rax
        jmp     do_something()       # TAILCALL

你可以看到源代码中只调用了do_something两次(除了对rax的奇怪处理),实际上没有提到do_something_elsegcc生成的代码几乎相同。


4
现代编译器利用有符号整数溢出为未定义行为的特性进行优化。因此,即使在CPU级别上行为是有定义的,也不能保证您的程序不会调用未定义的行为。 - ouah
@ouah:是的……添加了一条关于编译器在分析代码时可以进行的假设的说明。 - 6502
请问能否提供一个无符号下溢的规范链接定义?谢谢! - Thomas Weller
@ThomasWeller:n3690 表示“无符号整数应遵守算术模 2^n 的规律,其中 n 是该特定大小整数值表示中的比特数”,并作为注释“这意味着无符号算术不会溢出,因为无法用所得到的无符号整数类型表示的结果会被模减为比所得到的无符号整数类型能够表示的最大值更大的数字”。 - 6502
@6502:谢谢,答案已经编辑并引用了文档链接。 - Thomas Weller

3

在C++中,有符号整数溢出属于未定义行为。

例如:

INT_MIN - 1

-INT_MIN

这些表达式会导致未定义行为。

在一个16位的short和32位的int环境中,SHRT_MIN - 1-SHRT_MIN不属于未定义行为,因为通过整数提升,操作数首先被提升为int。但是在一个16位的shortint环境中,这些表达式也属于未定义行为。


1
通常是这样的。但由于这是C++,而C++受C++标准管制,您必须知道溢出是未定义行为。
尽管您所述的可能适用于大多数平台,但这并不是绝对的,因此不要依赖它。

3
signed 溢出是未定义的行为。 - mabraham

0

新值不需要是SHRT_MAX,它是未定义的。


1
行为未定义。程序很可能会崩溃。 - James Kanze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接