Bil*_*eal 25 c++ arithmetic-expressions overflow integer-promotion
考虑以下程序:
// http://ideone.com/4I0dT
#include <limits>
#include <iostream>
int main()
{
int max = std::numeric_limits<int>::max();
unsigned int one = 1;
unsigned int result = max + one;
std::cout << result;
}
Run Code Online (Sandbox Code Playgroud)
和
// http://ideone.com/UBuFZ
#include <limits>
#include <iostream>
int main()
{
unsigned int us = 42;
int neg = -43;
int result = us + neg;
std::cout << result;
}
Run Code Online (Sandbox Code Playgroud)
+运算符如何"知道"返回哪个正确的类型?一般的规则是把所有的参数转换为最广泛的类型,但在这里没有明确的"赢家"之间int和unsigned int.在第一种情况下,unsigned int必须选择作为结果operator+,因为我得到了结果2147483648.在第二种情况下,它必须选择int,因为我得到了结果-1.然而,在一般情况下,我没有看到这是如何可判定的.这是我看到的未定义的行为还是其他什么?
ild*_*arn 35
这在§5/ 9中明确概述:
许多期望算术或枚举类型的操作数的二元运算符会以类似的方式引起转换并产生结果类型.目的是产生一个通用类型,它也是结果的类型.此模式称为通常的算术转换,其定义如下:
- 如果任一操作数是类型
long double,则另一个操作数应转换为long double.- 否则,如果任一操作数是
double,则另一个操作数应转换为double.- 否则,如果任一操作数是
float,则另一个操作数应转换为float.- 否则,应对两个操作数执行整体促销.
- 然后,如果任一操作数是
unsigned long另一个应转换为unsigned long.- 否则,如果一个操作数是a
long int和另一个unsigned int,那么如果along int可以表示a的所有值unsigned int,unsigned int则应转换为along int; 否则两个操作数都应转换为unsigned long int.- 否则,如果任一操作数是
long,则另一个操作数应转换为long.- 否则,如果任一操作数是
unsigned,则另一个操作数应转换为unsigned.[ 注意:否则,唯一剩下的情况是两个操作数都是
int]
在两个场景中,结果operator+都是unsigned.因此,第二种情况是有效的:
int result = static_cast<int>(us + static_cast<unsigned>(neg));
Run Code Online (Sandbox Code Playgroud)
因为在这种情况下,值us + neg不能表示int,值result是实现定义的 - §4.7/ 3:
如果目标类型已签名,则如果可以在目标类型(和位字段宽度)中表示该值,则该值不会更改; 否则,该值是实现定义的.
Jer*_*fin 12
在C标准化之前,编译器之间存在差异 - 一些遵循"保值"规则,另一些遵循"签署保留"规则.保留符号意味着如果任一操作数是无符号的,则结果是无符号的.这很简单,但有时会给出相当惊人的结果(特别是当负数转换为无符号时).
C对更复杂的"保值"规则进行了标准化.在保值规则下,促销可以/确实取决于类型的实际范围,因此您可以在不同的编译器上获得不同的结果.例如,在大多数MS-DOS编译器上,它们int的大小short和long它们的大小相同.在许多当前系统int上,它们的大小与之相同long,并且short两者都不同.使用保值规则,这些可以导致促销类型在两者之间不同.
值保留规则的基本思想是,如果可以表示较小类型的所有值,它将提升为更大的签名类型.例如,16位unsigned short可以提升为32位signed int,因为每个可能的值unsigned short都可以表示为a signed int.当且仅当需要保留较小类型的值时,类型将被提升为无符号类型(例如,如果unsigned short和signed int都是16位,则a signed int不能代表所有可能的值unsigned short,因此unsigned short将被提升为unsigned int).
当您按原样分配结果时,结果将无论如何转换为目标类型,因此大多数情况相对较小 - 至少在大多数情况下,它只是将位复制到结果中,并且由您来决定是将其解释为已签名还是未签名.
当你没有分配比较结果时,事情会变得非常难看.例如:
unsigned int a = 5;
signed int b = -5;
if (a > b)
printf("Of course");
else
printf("What!");
Run Code Online (Sandbox Code Playgroud)
在符号保留规则下,b会被提升为无符号,并且在此过程中变得相等UINT_MAX - 4,所以"什么!" if将采取的腿.使用值保留规则,您可以设法产生一些类似于此的奇怪结果,但1)主要在类似DOS的系统中,int其大小short与2 相同,并且2)无论如何通常更难做到这一点.