【C++ 小问答】4:数值运算中的小类型隐式转换

如下哪段代码有整数溢出:

  1. 1int Func(short a, short b)
    2{
    3    return (a + b);
    4}
    
  2. 1int Func(signed char a, signed char b)
    2{
    3    return (a + b);
    4}
    
  3. 1long long int Func(int a, int b)
    2{
    3    return (a + b);
    4}
    
  4. 1long long int Func(int a, int b)
    2{
    3    return ((long long int)a + b);
    4}
    

导致整数溢出最常见的场景,是在数值计算中没有对较小类型的操作数做恰当的类型转换1。而上述的前三段代码看起来都涉及这个问题,因此还需要找其他原因。

cppinsights 上尝试转换以下代码:

1int main()
2{
3  unsigned char a = 127, b = 4;
4  int c = a + b;
5  unsigned char d = a + b;
6  
7  int x = 0xEFFFFFFF, y = 5;
8  long long z = x + y;
9}

生成如下结果:

 1int main()
 2{
 3  unsigned char a = 127;
 4  unsigned char b = 4;
 5  int c = static_cast<int>(a) + static_cast<int>(b);
 6  unsigned char d = static_cast<unsigned char>(static_cast<int>(a) + static_cast<int>(b));
 7  int x = 4026531839U;
 8  int y = 5;
 9  long long z = static_cast<long long>(x + y);
10}

可以看到,在做数值计算时,编译器总是会先把 unsigned char 类型的操作数转为 int,即便是在赋值对象也为 unsigned char 的情况下。查阅相关资料,在 cppreference 介绍隐式类型转换的页面 上找到了如下内容:

Integral promotion

prvalues of small integral types (such as char) may be converted to prvalues of larger integral types (such as int). In particular, arithmetic operators do not accept types smaller than int as arguments, and integral promotions are automatically applied after lvalue-to-rvalue conversion, if applicable.

可知,数值运算符以 int 为可接受的最小类型。正因如此,所有比 int 小的整数类型都会在计算前先转为 int。问题中的选项 1、2 都属于这种情况,而选项 3 中的操作数不会做类型提升,因此有整数溢出的风险。


  1. 标准做法是先提升操作数类型再进行计算(参考选项 4),以避免获得小类型的中间结果,以及在其之上发生的数值溢出。 ↩︎


打印 C++ 类的内存排布
用 GDB 调试动态链接库