整型变量是一个家族:短整型 short(2 字节)、整型 int(4 字节,日常默认就用它)、长整型 long、超长整型 long long(8 字节)。每种都有范围上限,装不下就溢出——而且不会报错,会"回绕"成一个莫名其妙的数(INT_MAX + 1 变成 INT_MIN,正数一秒变负数)。记住三条:用 sizeof 查字节数;用 <climits> 里的 INT_MAX 查范围;算钱、算大数一律上 long long。
一、整型家族一览(32/64 位 Windows 常见情况)
| 类型 | 字节 | 大致范围 | 什么时候用 |
|---|---|---|---|
short(short int) |
2 | −32768 ~ 32767 | 省内存的场合(嵌入式、大数组) |
int |
4 | −21.4 亿 ~ 21.4 亿 | 默认选择,够用就用它 |
long(long int) |
Windows 4 / Linux 64 位 8 | 视平台而定 | ⚠ 跨平台时不要依赖它 |
long long |
8 | −922 京 ~ 922 京 | 装大数:钱、时间戳、ID |
每种都有"无符号版":unsigned int、unsigned short…(省略 int 时写作 unsigned)。无符号的特点是没有负数,正数范围翻倍(unsigned int 能到约 42.9 亿)。
这个表要背的只有一列:short 2、int 4、long long 8。long 那一行记住"不可靠"就够了——它在 Windows 下是 4 字节、在 64 位 Linux 下是 8 字节,是真实存在的移植坑。
二、用 sizeof 亲眼看看
cout << sizeof(short) << endl; // 2
cout << sizeof(int) << endl; // 4
cout << sizeof(long) << endl; // Windows 4 / Linux 8
cout << sizeof(long long) << endl; // 8
sizeof 不是函数,是运算符——它直接问编译期"这个东西占几字节"。写成 sizeof(int) 或 sizeof(a) 都行(sizeof(a) 也可以写 sizeof a)。
三、查范围:<climits> 里的"最大的数"
#include <climits> // 或者老写法 <limits.h>
cout << INT_MAX << endl; // 2147483647
cout << INT_MIN << endl; // -2147483648
cout << SHRT_MAX << endl; // 32767
背不住具体数字没关系,记住这个头文件:不确定某个类型够不够大时,去查 XXX_MAX / XXX_MIN。
四、核心风险:溢出(Overflow)
int big = INT_MAX; // 21 亿多,这是 int 的极限
big = big + 1; // 加一以后会怎样?
cout << big << endl; // 输出 -2147483648 ← 变成了巨大的负数!
这就是溢出:值超出了类型的范围,多出来的部分"从头绕回去"了(像里程表走满一圈回到 0)。
三个必须记住的事实:
- 不会报错、不会警告(多数编译器默认不提示)——它安安静静地给你错结果
- 结果看起来毫无道理:正数变负数,大数变小
- 这是真实事故的常见来源:库存数量、账户余额、游戏金币、页面浏览量一旦溢出,就会同时影响计算和判断(
if (金币 > 0)的判断也会一起失效)
实战规矩:
| 场景 | 选择 |
|---|---|
| 一般计数(人数、次数) | int 够用 |
| 金额(以"分"为单位) | long long —— 21 亿分=2100 万元,很容易超 |
| 时间戳、ID | long long |
| 一定不会有负数的计数 | 可以用 unsigned,但减法要特别小心 |
unsigned 的陷阱(提前认识一下):
unsigned int u = 0;
u = u - 1; // 不会变成 -1!
cout << u; // 输出 4294967295(变成了最大的无符号数)
所以:无符号类型"看起来安全"(不会有负数),但一旦不小心减过头,会跳成一个巨大的正数——比有符号的溢出更隐蔽。新手建议:不确定就别用 unsigned。
五、动手 Demo:把溢出"抓个现行"
新建 intvar.cpp:
#include <iostream>
#include <climits> // INT_MAX / SHRT_MAX 在这里
using namespace std;
int main() {
cout << "int 占 " << sizeof(int) << " 字节" << endl;
cout << "int 最大 = " << INT_MAX << endl;
int big = INT_MAX;
cout << "加一之前:" << big << endl;
big = big + 1;
cout << "加一之后:" << big << endl; // ⚠ 观察这一行
short s = SHRT_MAX;
cout << "short 最大 = " << s << " 加一后 = " << (short)(s + 1) << endl;
long long money = 3000000000LL; // 30 亿分 = 3000 万元
cout << "long long 装得下:" << money << endl;
return 0;
}
看到什么算成功:加一之后 输出一个大负数(一般是 -2147483648)。这就是溢出,悄无声息地发生了。
30 秒实验:把 long big 换成 short big,先赋 SHRT_MAX 再 +1,看它从上万直接跳到 −32768。"回绕"就这么直白。
六、常见误区
- "超范围会报错"——不会。溢出不报错、不警告,直接给错值。这是它最危险的地方
- "手机号用
int存就行"——绝对不行。手机号 11 位,超过 21 亿。要么long long,要么按字符串存 - "
unsigned比int更安全"——恰恰相反:unsigned减过头会跳到极大的正数,比负数更隐蔽。而且"有符号与无符号混算"还会触发类型提升的坑(第 3 章) - "
long就是 8 字节"——在 Windows 上它常常是 4 字节。要 8 字节的大数,明确写long long,别指望long
自测四问(点开对答案)
1. `short` / `int` / `long long` 各占几字节(Windows)?——2 / 4 / 8。 2. `INT_MAX + 1` 会得到什么?——通常是 `INT_MIN`(−2147483648),**溢出回绕**成了大负数,且不报错。 3. 手机号、金额该怎么存?——**别用 `int`**;金额用 `long long`(以"分"为单位),手机号用 `long long` 或字符串。 4. 查一个类型的最大值该找哪儿?——引入 `- ☑ 实型变量