oru*_*pov 14 c integer-overflow
我在C规范中读到一点,无符号变量(特别是unsigned short int)执行一些所谓的整数溢出回绕,虽然我找不到任何有符号变量,除了我留下未定义的行为.我的教授告诉我他们的价值观也被包围了.(也许他只是意味着gcc)我认为这些位被截断了,我留下的位给了我一些奇怪的价值?!那么,任何人都可以解释什么是环绕的,它与仅截断位有什么不同.
AnT*_*AnT 20
有符号整数变量在C语言中没有环绕行为.算术计算期间的有符号整数溢出会产生未定义的行为.请注意,您提到的GCC编译器在优化中实现严格的溢出语义是已知的,这意味着它利用了这种未定义的行为情况提供的自由:GCC编译器假定有符号整数值永远不会回绕.这意味着GCC实际上恰好是您不能依赖有符号整数类型的环绕行为的编译器之一.
例如,GCC编译器可以假设对于变量int i具有以下条件
if (i > 0 && i + 1 > 0)
Run Code Online (Sandbox Code Playgroud)
相当于一个人
if (i > 0)
Run Code Online (Sandbox Code Playgroud)
这正是严格溢出语义的含义.
无符号整数类型实现模运算.模数相等2^N,其中N是类型的值表示中的位数.因此,无符号整数类型确实似乎在溢出时回绕.
但是,C语言从不在小于int/的域中执行算术计算unsigned int.unsigned short int您在问题中提到的类型通常会int在任何计算开始之前被提升为键入表达式(假设范围unsigned short适合于范围int).这意味着1)计算unsigned short int将在域中执行int,溢出时发生int溢出,2)在此类计算期间溢出将导致未定义的行为,而不是环绕行为.
例如,此代码生成环绕
unsigned i = USHRT_MAX;
i *= INT_MAX; /* <- unsigned arithmetic, overflows, wraps around */
Run Code Online (Sandbox Code Playgroud)
而这段代码
unsigned short i = USHRT_MAX;
i *= INT_MAX; /* <- signed arithmetic, overflows, produces undefined behavior */
Run Code Online (Sandbox Code Playgroud)
导致未定义的行为.
如果没有int发生溢出并且结果被转换回unsigned short int类型,它将再次以模数减少2^N,这看起来好像值已经被包围.
Joh*_*ode 10
想象一下,你的数据类型只有3位宽.这允许您表示8个不同的值,从0到7.如果添加1到7,您将"回绕"回到0,因为您没有足够的位来表示值8(1000).
对于无符号类型,此行为已明确定义.它没有为签名类型定义良好,因为有多种方法可以表示有符号值,并且溢出的结果将根据该方法进行不同的解释.
符号幅度:最高位表示符号; 0表示正数,1表示负数.如果我的类型再次是三位宽,那么我可以表示如下的有符号值:
000 = 0
001 = 1
010 = 2
011 = 3
100 = -0
101 = -1
110 = -2
111 = -3
Run Code Online (Sandbox Code Playgroud)
由于符号占用一位,因此我只有两位来编码0到3的值.如果我加1到3,我将溢出-0作为结果.是的,有两个表示0,一个正面和一个负面.您不会经常遇到符号幅度表示.
一个补码:负值是正值的按位反转.再次,使用三位类型:
000 = 0
001 = 1
010 = 2
011 = 3
100 = -3
101 = -2
110 = -1
111 = -0
Run Code Online (Sandbox Code Playgroud)
我有三位来编码我的值,但范围是[-3,3].如果我加1到3,我将溢出-3作为结果.这与上面的符号幅度结果不同.同样,使用此方法有两种编码为0.
二进制补码:负值是正值的按位反转加1.在三位系统中:
000 = 0
001 = 1
010 = 2
011 = 3
100 = -4
101 = -3
110 = -2
111 = -1
Run Code Online (Sandbox Code Playgroud)
如果我加1到3,那么我将溢出-4,这与前两种方法不同.请注意,我们有一个稍大的值范围[-4,3],只有一个表示为0.
二进制补码可能是表示有符号值的最常用方法,但它不是唯一的,因此C标准无法保证溢出有符号整数类型时会发生什么.因此它保留了未定义的行为,因此编译器不必处理解释多个表示.
在不确定的行为来自于早期的便携性问题,当符号整型可以既表现为符号和幅度,一个补码或二进制补码。
如今,所有架构都将整数表示为可以环绕的二进制补码。但要小心:因为你的编译器假设你不会运行未定义的行为是正确的,所以当优化打开时你可能会遇到奇怪的错误。