将临时值存储到某种数据类型时,算术运算的标准规则是什么?

Vin*_*rta 3 c++ standards integer-overflow compiler-warnings language-lawyer

我有一些代码,我很久以前做过(当时我正在使用visual studio 2003).现在我正在使用gcc并且一些值已经溢出,我看看发生了什么,这让我感到惊讶.

让我来说明发生了什么:

作品(输出= 85):

int b = 35000000;
unsigned long a = 30000000;
unsigned long n = ( 100 * a ) / b;
Run Code Online (Sandbox Code Playgroud)

不起作用(溢出):

int b = 35000000;
int a = 30000000;
unsigned long n = ( 100 * a ) / b;
Run Code Online (Sandbox Code Playgroud)

不起作用(溢出):

int b = 35000000;
unsigned long n = ( 100 * 30000000 ) / b;
Run Code Online (Sandbox Code Playgroud)

这一切都应该是正确的.现在有什么不好的是:

unsigned long b= 35000000;
unsigned long n = ( 100 * 30000000 ) / b;
Run Code Online (Sandbox Code Playgroud)

曾经工作!现在它没有.

好吧,实际上它仍适用于微软编译器,但它不适用于clang和gcc.如果您愿意,可以使用不同的编译器进行编译:http://rextester.com/BZU89042

  • Output = 85 - 用于x86的Microsoft(R)C/C++优化编译器版本19.00.23506
  • Output = 527049830640 - g ++ 5.4.0
  • Output = 527049830640 - 铿锵3.8.0

关于这些的标准C++规则是什么?

Bar*_*rry 6

确定整数文字类型的规则来自[lex.icon]:

整数文字的类型是表7中相应列表中的第一个,其中可以表示其值.

其中,无后缀的类型列表int,然后long intlong long int.之后,当我们进行数学运算时,规则始终是"通常的算术转换",它们在[expr]中枚举:

此模式称为通常的算术转换,其定义如下:

(11.1)如果任一操作数是作用域枚举类型,则不执行任何转换; 如果另一个操作数的类型不同,则表达式格式不正确.

(11.2)如果任一操作数的类型为long double,则另一个操作数应转换为long double.

(11.3)否则,如果任一操作数为double,则另一个操作数应转换为double.

(11.4)否则,如果任一操作数是浮点数,则另一个操作数应转换为浮点数.

(11.5)否则,应对两个操作数执行整体促销.63然后,对促进的操作数应适用以下规则:

(11.5.1)如果两个操作数具有相同的类型,则不需要进一步转换.

(11.5.2)否则,如果两个操作数都有有符号整数类型或两者都有无符号整数类型,则具有较小整数转换等级类型的操作数应转换为具有较大等级的操作数的类型.

(11.5.3)否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则带有符号整数类型的操作数应转换为具有无符号整数的操作数的类型类型.

(11.5.4)否则,如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数应转换为操作数的类型.有符号整数类型.

(11.5.5)否则,两个操作数都应转换为与带符号整数类型的操作数类型相对应的无符号整数类型.


我们来看看你的例子:

int b = 35000000;
unsigned long a = 30000000;
unsigned long n = ( 100 * a ) / b;
Run Code Online (Sandbox Code Playgroud)

这很好,因为类型100 * aunsigned long(因为11.5.3),它足够宽以容纳该结果.

其余的不起作用,因为乘法产生一种类型int.两个ints相乘得到一个int(因为11.5.1),并且在第一种情况下我们明确地声明aint和在其他情况下文字30000000具有类型int(因为它足够小以表示int)

请注意,在最后一个示例中:

unsigned long b= 35000000;
unsigned long n = ( 100 * 30000000 ) / b;
Run Code Online (Sandbox Code Playgroud)

无论是b或者n声明都没关系,unsigned long表达式(100 * 30000000)仍然是两个int乘以一起,因此int无论表示如何都有类型.gcc和clang都警告过这种溢出.

要修复它,您始终可以为文字添加后缀.在这种情况下,要么100u30000000u将要做的伎俩.这使得文字类型unsigned int(per [lex.icon]),它产生乘法的类型unsigned int(per [expr] /11.5.3),它不会溢出.