C++必看:符号与溢出问题

在C++中,整数类型分为有符号整数(signed)和无符号整数(unsigned),它们的主要区别在于是否能表示负数。下面我会详细说明各种整数类型的存储方式、范围,以及溢出时的行为。


1. C++中的整数类型

C++标准定义了几种基本整数类型,每种类型的大小(字节数)可能因编译器和平台而异,但通常遵循以下约定(以常见 32 位或 64 位系统为例):

类型典型大小(字节)有符号范围无符号范围
char1 (8位)-128 到 1270 到 255
short2 (16位)-32,768 到 32,7670 到 65,535
int4 (32位)-2,147,483,648 到 2,147,483,6470 到 4,294,967,295
long4 或 8平台相关,通常同 int 或更大平台相关
long long8 (64位)-2^63 到 2^63-10 到 2^64-1
  • 有符号类型:默认情况下,charshortintlonglong long 是有符号的。
  • 无符号类型:通过添加 unsigned 修饰符,如 unsigned intunsigned char 等。
  • 特殊情况char 可以是有符号(signed char)或无符号(unsigned char),具体取决于编译器实现。

2. 存储方式

整数在内存中的存储方式与字节序(大端或小端,前面已解释)和符号表示方法有关。

有符号整数的存储

  • 表示方法:使用二进制补码(Two’s Complement)。
    • 正数:直接用二进制表示。
    • 负数:取正数的二进制反码加 1。
  • 示例(以 8 位 signed char 为例):
    • 12701111111
    • 12810000000
    • 11111111100000001 取反后加 1)

无符号整数的存储

  • 表示方法:直接用二进制表示,没有符号位,所有位都用于表示数值。
  • 示例(以 8 位 unsigned char 为例):
    • 000000000
    • 25511111111

范围计算

  • 有符号:n 位能表示 2^(n-1)2^(n-1)-1
  • 无符号:n 位能表示 02^n-1

3. 溢出问题

溢出指的是整数运算结果超出了类型所能表示的范围。C++中,有符号和无符号整数的溢出行为不同。

有符号整数溢出

  • 行为:有符号整数溢出是未定义行为(Undefined Behavior, UB)。

    • 编译器可以做任何事(如崩溃、返回错误值、忽略溢出等),不可预测。
  • 示例

    #include <iostream>
    int main() {
        int a = 2147483647; // int 最大值
        std::cout << a + 1 << std::endl; // 溢出,结果未定义
    }
    
    
    • 可能输出 2147483648(循环到最小值),但不能依赖这种行为。
  • 原因:补码表示下,溢出可能导致符号位翻转,但标准不强制要求这种“环绕”行为。

无符号整数溢出

  • 行为:无符号整数溢出是定义明确的行为,遵循模运算(取模 2^n,其中 n 是位数)。

    • 超出最大值时,从 0 开始循环;低于 0 时,从最大值开始循环。
  • 示例

    #include <iostream>
    int main() {
        unsigned int a = 4294967295; // unsigned int 最大值
        std::cout << a + 1 << std::endl; // 输出 0
        std::cout << a - 4294967296 << std::endl; // 输出 4294967295
    }
    
    
    • 加 1:4294967295 + 1 = 0(模 2^32)。
    • 减 1:0 - 1 = 4294967295(模 2^32)。

实际影响

  • 有符号溢出:可能导致程序错误,需避免(如检查边界或使用更大类型)。
  • 无符号溢出:常用于循环计数器等场景,但需注意逻辑错误。

4. 注意事项

  1. 类型提升
    • 在混合运算中(如 intunsigned int),有符号类型可能被提升为无符号,导致意外结果。

    • 示例:

      int a = -1;
      unsigned int b = 1;
      std::cout << (a < b) << std::endl; // 输出 0(false)
      
      
      • a 被提升为无符号,变为 4294967295,大于 1
  2. 编译器警告
    • 开启 Woverflow(如在 GCC/Clang 中)可以检测有符号溢出。
  3. 解决溢出
    • 使用更大的类型(如 long long)。

    • 使用标准库(如 <limits>)检查范围:

      #include <limits>
      std::cout << std::numeric_limits<int>::max() << std::endl; // int 最大值
      
      

5. 总结与记忆

  • 有符号
    • 范围:2^(n-1)2^(n-1)-1
    • 存储:补码形式。
    • 溢出:未定义行为,小心使用。
  • 无符号
    • 范围:02^n-1
    • 存储:直接二进制。
    • 溢出:模运算,循环安全。
  • 口诀
    • “有符号带负补码存,溢出乱来莫依赖;无符号全正模运算,溢出循环很可爱。”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值