【基础-数的表示】C/C++基本类型表示范围
C/C++ 的数据类型表示范围取决于编译器和硬件平台,但标准规定了最小范围,实际范围可通过 limits.h 和 float.h 获取。以下以最常见的 32/64 位系统(LP64/ILP32/LLP64) 为准,列出典型范围。
1. 整型(整数类型)
1.1 有符号整型
| 类型 | 位数 | 最小值 | 最大值 |
|---|---|---|---|
signed char |
8 | -128 | 127 |
short (或 short int) |
16 | -32,768 | 32,767 |
int |
32 | -2,147,483,648 | 2,147,483,647 |
long (Linux 64位: 64位; Windows 64位: 32位) |
32/64 | -2,147,483,648 / -9.22e18 | 2,147,483,647 / 9.22e18 |
long long |
64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
1.2 无符号整型
| 类型 | 位数 | 最小值 | 最大值 |
|---|---|---|---|
unsigned char |
8 | 0 | 255 |
unsigned short |
16 | 0 | 65,535 |
unsigned int |
32 | 0 | 4,294,967,295 |
unsigned long |
32/64 | 0 | 4.29e9 / 1.84e19 |
unsigned long long |
64 | 0 | 18,446,744,073,709,551,615 |
⚠️ 注意:
char是否有符号由实现定义(通常为signed char,但有些 ARM 工具链默认为unsigned char)。
1.3 整数存储
计算机内部只能存储0和1(二进制),所以需要用二进制来表示数字。但数字有正数和负数,如何用二进制表示负数?
三种编码方式应运而生:原码、反码、补码。
结论: 计算机中存储和运算都是用补码,原码和反码只是理解补码的中间步骤。
2.1 原码(Sign-Magnitude)
定义: 最高位表示符号(0=正,1=负),其余位表示数值的绝对值。
| 十进制 | 原码(8位) |
|---|---|
| +5 | 0 0000101 |
| -5 | 1 0000101 |
| +0 | 0 0000000 |
| -0 | 1 0000000 |
特点:
直观易懂,人一眼能看出大小
有两个0(+0和-0),浪费了一个编码
减法无法直接用加法器实现(需要额外处理符号位)
2.2 反码(One's Complement)
定义:
- 正数:反码 = 原码
- 负数:反码 = 原码符号位不变,其他位取反
| 十进制 | 原码(8位) | 反码 |
|---|---|---|
| +5 | 0 0000101 | 0 0000101 |
| -5 | 1 0000101 | 1 1111010 |
| +0 | 0 0000000 | 0 0000000 |
| -0 | 1 0000000 | 1 1111111 |
特点:
正数不变,负数按位取反
仍然有两个0(+0和-0)
减法可以通过加法实现(但需要循环进位)
2.3 补码(Two's Complement)★★★★★
定义:
- 正数:补码 = 原码(=反码)
- 负数:补码 = 反码 + 1
更快的口诀: "原码取反,再加一"(针对负数)
| 十进制 | 原码 | 反码 | 补码 |
|---|---|---|---|
| +5 | 0 0000101 | 0 0000101 | 0 0000101 |
| -5 | 1 0000101 | 1 1111010 | 1 1111011 |
| +0 | 0 0000000 | 0 0000000 | 0 0000000 |
| -0 | 1 0000000 | 1 1111111 | 1 00000000(溢出,仍为0) |
特点(必须背住!):
只有1个0(解决了"两个0"的问题)
减法可以用加法器统一实现
计算机中实际存储和运算用的就是补码!
8位补码的范围:-128 ~ +127
| 编码方式 | 正数表示 | 负数表示 | 0的个数 | 范围(8位) | 是否用于存储 |
|---|---|---|---|---|---|
| 原码 | 符号位0+绝对值 | 符号位1+绝对值 | 2个(+0和-0) | -127 ~ +127 | ❌ |
| 反码 | 同原码 | 符号位不变,其余取反 | 2个(+0和-0) | -127 ~ +127 | ❌ |
| 补码 | 同原码 | 反码+1 | 1个(只有0) | -128 ~ +127 | ✅ |
2. 浮点类型(IEEE 754)
| 类型 | 位数 | 最小值(正) | 最大值 | 精度(有效十进制位数) |
|---|---|---|---|---|
float |
32 | ≈1.175 × 10⁻³⁸ | ≈3.402 × 10³⁸ | 6~7 位 |
double |
64 | ≈2.225 × 10⁻³⁰⁸ | ≈1.798 × 10³⁰⁸ | 15~16 位 |
long double |
80 / 128 (x86扩展精度) | ≈3.36 × 10⁻⁴⁹³² | ≈1.18 × 10⁴⁹³² | 18~19 位 |
特殊值:
float/double还可表示正负无穷(±INF)、NaN(非数值)、零(有符号零 ±0.0)。

阶码的范围是0-(2^n-1),全1有特殊意义,表示无穷大。

对于规格化单精度:

其中 bibi 是尾数位从左到右的比特。
对于双精度 (64位):
- 指数位 11 位,偏移量 1023
- 尾数位 52 位
- 公式同理,只需替换偏移量和位数。
3. 布尔型与字符类型
| 类型 | 取值 | 说明 |
|---|---|---|
bool (C++ / C99) |
true (1) 或 false (0) |
占 1 字节(典型) |
char |
-128~127 或 0~255 | 用于存储 ASCII 或 UTF-8 字节 |
wchar_t |
平台相关(16/32位) | 用于宽字符 |
4. void 类型
-
void:没有值,不能表示范围。用于无返回值函数或通用指针void*。
5. 如何获取本机的精确范围?
IEEE 754 通过将 S、M、E 编码到固定的二进制位域中来实现存储。
例如:E=127 → 实际指数 0,E=128 → 实际指数 1,E=126 → 实际指数 -1。
(2) 非规格化值(Denormalized / Subnormal)
当 E == 0 且 M ≠ 0 时:
(3) 零值
当 E == 0 且 M == 0 时:
(4) 无穷大(Infinity)
当 E == 255 且 M == 0 时:
(5) 非数值(NaN, Not a Number)
当 E == 255 且 M ≠ 0 时:
3.2 双精度(64 位)
包含头文件 <limits.h>(整型)和 <float.h>(浮点型)。
-
示例:
#include <stdio.h> #include <limits.h> #include <float.h> int main() { printf("int max: %d\n", INT_MAX); printf("double min positive: %e\n", DBL_MIN); return 0; }也可用 C++ 的
<limits>模板:std::numeric_limits<int>::max();6. 标准最低要求(ISO C/C++ 保证)
类型 最小位数 最小值 最大值 signed char8 -127 127 short16 -32,767 32,767 int16 -32,767 32,767 long32 -2,147,483,647 2,147,483,647 long long(C99/C++11)64 -9.22e18 9.22e18 注意:标准允许采用补码或原码,现代平台全是补码。
任意一个二进制浮点数
V可表示为:V = (-1)ˢ × M × 2ᴱ
-
S:符号位(Sign),0 表示正数,1 表示负数。
-
M:尾数(Significand 或 Mantissa),是一个二进制小数,范围通常为 [1, 2) 或 [0, 1),取决于指数部分。
-
E:指数(Exponent),对尾数进行缩放,可以是正数或负数。
-
IEEE 754 定义了多种浮点格式,最常用的是:
类型 总位数 符号位 指数位 尾数位 指数偏移(Bias) 单精度(binary32) 32 1 8 23 127 双精度(binary64) 64 1 11 52 1023 半精度(binary16) 16 1 5 10 15 四精度(binary128) 128 1 15 112 16383 3.1 单精度(32 位)
[31] 符号位 S [30:23] 指数位 E (8 位) [22:0] 尾数位 M (23 位)表示的值分为几种情况(根据指数 E 的值):
(1) 规格化值(Normalized)
当 0 < E < 255(即 E 不全为 0 也不全为 1)时:
-
实际指数 = E - Bias,Bias = 127。
-
尾数隐含整数部分为 1,即实际尾数 = 1.M(二进制)。
-
因此值 = (−1)S×1.M(2)×2E−127(−1)S×1.M(2)×2E−127。
-
实际指数 = 1 - Bias = -126(固定)。
-
尾数隐含整数部分为 0,即实际尾数 = 0.M。
-
值 = (−1)S×0.M×2−126(−1)S×0.M×2−126。
-
作用:表示非常接近零的数,实现渐进下溢(gradual underflow),避免突然下溢到零。
-
表示 ±0(符号位决定正负零)。正零和负零在算术上通常视为相等。
-
表示 ±∞(符号位决定)。用于溢出结果,如 1.0/0.0。
-
表示未定义或无效的运算结果,如 0.0/0.0,∞ - ∞ 等。
-
可分类为 SNaN(Signaling NaN,会触发异常)和 QNaN(Quiet NaN,不触发异常),通常由 M 的最高位区分。
-
[63] 符号位 S [62:52] 指数位 E (11 位) [51:0] 尾数位 M (52 位)Bias = 1023。
-
规格化范围:E 从 1 到 2046,实际指数 = E - 1023。
-
非规格化:E = 0,M ≠ 0,实际指数 = -1022。
-
无穷大:E = 2047,M = 0。
-
NaN:E = 2047,M ≠ 0。
-
7. 常见陷阱
-
隐式类型转换:有符号与无符号混合计算时,有符号会被转为无符号,可能导致意外结果(如
-1 > 0U为真)。 -
溢出:有符号溢出是未定义行为;无符号溢出是模 2^n 运算。
-
浮点数比较
不要直接使用
==比较两个浮点数,因为累积误差会导致本来相等的值微量不同。应使用:fabs(a - b) < epsilon整数转浮点的精度丢失
当整数超过 2²⁴(单精度)或 2⁵³(双精度)时,无法精确表示所有整数,会产生舍入。
例:16777217 在单精度中会变成 16777216。运算顺序影响精度
例如
(a+b)+c与a+(b+c)可能因舍入顺序不同导致结果有微小差异。非规格化数性能问题
有些 CPU 在处理非规格化数时速度较慢(甚至产生异常),可通过设置
FTZ(Flush To Zero)标志强制将非规格化数视为 0 来提高性能(通常在图形或音频处理中)。 -
例题
-
一个 32位无符号整数可以表示的最大值,最接近下列哪个选项? A. 4×10^9 [*] B. 4×10^10 C. 2×10^9 D. 2×10^10解析:两种思路,一是记住32位无符号数的表示范围为0-4,294,967,295,约为4x10^9;
-
二是使用更通用的方法。计算一个十进制位需要多少个二进制位,虽然10不能使用整个二进制位表示,但可以按下面的思路推导出来。
-
一个4进制位,可以用2个二进制位表示,因为log2(4)=2
-
一个8进制位,可以用3个二进制位表示,因为log2(8)=3
-
一个16进制位,可以用4个二进制位表示,因为log2(16)=4
-
.....
-
那么一个10进制位,可以用多个少个二进制位表示呢?同样,可以按上面的计算方法,log2(10)≈3.22.
-
32位无符号数可以表示为4x2^30. 30/3.22≈9.316≈9(舍0.316),因此,如果答案在A/B之间,应该选择A。同样,也可以表示成2x2^31. 31/3.22≈9.627≈10(入0.373),如果答案在c/d之间,应该选择D。显然,前者舍0.316,后者入0.373,前者更接近真实值。
-
32 位 int 类型的存储范围是( )? A. -2147483647 ~ +2147483647 B. -2147483647 ~ +2147483648 C. -2147483648 ~ +2147483647 D. -2147483648 ~ +2147483648解析:整数分为无符号数和有符号数,无符号数的范围为0-(2^n-1);有符号的范围为-2^(n-1)-(2^(n-1)-1).最小的负数是-2^(n-1),二进制表示为1000.....0.
8. 备考
(1)核心背诵表
上面的内容必须滚瓜烂熟
(2) 典型题型分类与解法
题型1:数据溢出计算(“算”题型)
特征:给一个超出范围的数,问赋值后或运算后结果是多少(通常会变成负数或截断)。
-
真题变式:
short a = 32767; a++; printf("%d", a);输出什么?-
解析:32767是short的最大值(二进制
0111 1111 1111 1111),加1变成1000 0000 0000 0000,在补码中代表 -32768。
-
-
计算技巧:超出最大值会“循环”到最小值。例如
short里32767 + 1 = -32768;32767 + 2 = -32767。
题型2:类型比较与整型提升(“比”题型)
特征:比较 int 和 unsigned int,或者 char 和 unsigned char 的大小,判断 if 语句真假。
-
核心规则(整型提升):当
int和unsigned int运算或比较时,int会被隐式转换为unsigned int。 -
经典陷阱题:
unsigned int a = 10; int b = -20; if (a > b) { printf("A"); } else { printf("B"); }-
解析:输出结果是 B。因为
b被转换成很大的无符号数(-20的补码变成4294967276),导致a > b为假。
-
题型3:内存大小与范围推断(“识”题型)
特征:给出字节数(如2字节、4字节),问该类型能表示的最大值或最小值。
-
公式:
- 无符号最大值:2n−12n−1(n为位数,如8位char就是 28−1=25528−1=255)。
- 有符号最大值:2n−1−12n−1−1(如16位short就是 215−1=32767215−1=32767)。
- 有符号最小值:−2n−1−2n−1(如16位short就是 −32768−32768)。
-
易错点:为什么负数绝对值比正数大1?因为补码中
1000...000代表 −2n−1−2n−1,而正数没有对应的+2^{n-1}$(会溢出)。
三、 代码阅读题中的混合考查
在阅读程序大题中,它不会单独问范围,而是隐藏在循环或数组下标中:
-
死循环陷阱:
for (char i = 0; i < 128; i++) { ... }-
解析:当
char为 signed 时,i最大到127,再加1变成 -128,永远满足i < 128,导致死循环。
-
-
数组下标越界:
unsigned char a[256]; for (int i = 0; i <= 255; i++) a[i] = i;-
解析:当
i=255赋值正常,但循环条件i<=255对于unsigned char是恒成立的(255+1溢出变0),会导致数组越界访问。
-
💎 备考小贴士
- 拒绝口算:遇到边界值(如 32767、-2147483648),一定要在草稿纸上画二进制草图,补码的进位关系非常容易看错。
- 关键词敏感:看到
unsigned,立刻警惕“无符号与有符号比较”的陷阱。 - char特判:看到
char类型的循环,要立刻判断它是否会因为127+1=-128或255+1=0导致死循环或溢出。
这里有一道题留给你思考,试试能不能快速判断答案:
题目:在32位系统中,定义
unsigned short x = 65535;,执行x += 2;后,x的值是?
A. 65537
B. 1
C. 65534
D. 0
答案是 B(因为无符号short范围0~65535,65535 + 1 = 0,+ 2 = 1)。如果你做对了,说明你已经掌握了最核心的溢出规律!如果有不理解的地方,随时可以再问我。
更多推荐


所有评论(0)