C 语言中数据类型及类型转换
- 操作数本身没有数据类型,数据类型取决于指令

C 语言的整型变量及取值范围
NOTE
无论是有符号数还是无符号数,C 语言并不检测数据在加、减、乘等运算中产生的溢出现象。 溢出由程序员编写的应用程序进行判断和检查。
- 基本整数类型:
char:字符类型,通常占用 1 字节。其有无符号性由编译器决定,可显式声明为signed char或unsigned char。short [int]:短整型,通常占用 2 字节。int:整型,通常占用 4 字节,是 C 语言中最常用的整数类型。long [int]:长整型,通常占用 4 字节或 8 字节(在 64 位系统上通常为 8 字节)。- `long long [int] 整型,C99 标准引入,至少占用 8 字节。
- 有符号与无符号:
- 除了
char类型外,其他整型默认均为有符号类型(signed)。例如int等同于signed int。 - 通过
unsigned关键字可声明为无符号类型,例如unsigned int。无符号数只能表示非负值,其所有位都用于表示数值。 - 有符号数通常采用补码形式表示。最高位为符号位(0 表示正数,1 表示负数)。
- 除了
- 各类型典型大小 (字节) 及取值范围 (具体取决于系统和编译器):
| 类型 | 典型大小 (字节) | 范围 (示例) |
|---|---|---|
char | 1 | [-128, 127] 或 [0, 255] |
short | 2 | [-32768, 32767] |
int | 4 | [-2147483648, 2147483647] |
long | 4 或 8 | [-2147483648, 2147483647] 或 [-9E18, 9E18] (约) |
long long | 8 | [-9E18, 9E18] (约) |
unsigned char | 1 | [0, 255] |
unsigned int | 4 | [0, 4294967295] |
- 特殊整数类型:
size_t:sizeof运算符的结果类型,为无符号整型,用于表示内存大小或数组索引。ptrdiff_t:两个指针相减的结果类型,为有符号整型。
不同字长整数之间的转换
- 相同字长之间的转换:
- 保持机器码不变
- 示例:
char c = -127; unsigned char uc = (unsigned char) c;。转换后机器码不变,符号位被当作数值位。uc = 129
- 小字长向大字长转换 (整型提升/拓宽转换):
- 当较小的整型(如
char或short)用于表达式时,它们通常会被自动转换为int或unsigned int(通常为int,除非原类型的值无法用int表示)。这有助于提高 CPU 运算效率。 - 有符号数: 进行符号位扩展 (Sign Extension)。新分配的高位字节会填充原数值的符号位(如果原符号位是 0,则填充 0;如果原符号位是 1,则填充 1)。
- 无符号数: 进行零扩展 (Zero Extension)。新分配的高位字节会全部填充 0。
- 当较小的整型(如
- 大字长向小字长转换 (截断转换):
- 当一个大字长的整数赋值给一个小字长的整数类型时,会发生截断。
- 转换结果: 高位字节会被直接丢弃,只保留低位字节。
- 潜在问题:
- 数据丢失/溢出: 如果原数值超出了目标类型所能表示的范围,则会发生数据丢失,导致值改变或溢出。
- 符号改变: 对于有符号数,截断后最高位可能变为 1,导致正数变为负数,或负数变为一个完全不同的值。
- 示例:
int i = 257; char c = (char)i;。由于257的二进制表示是...00000001 00000001(假设int4 字节),截断为char(1 字节) 后,只保留低 8 位00000001,因此c的值为 1。
有符号数和无符号数转换
- 隐式转换规则:
- 当有符号数与无符号数在表达式中混合运算时,C 语言会执行整型提升,然后将有符号数隐式转换为无符号数。
- 转换的前提是无符号数的类型不小于有符号数的类型。如果无符号数类型较小,则会先进行整型提升,再进行有符号 - 无符号转换。
- 转换结果: 转换过程中,数的位模式保持不变,但其解释方式会发生改变。
- 转换影响:
- 正数转换为无符号数: 值保持不变。
- 负数转换为无符号数: 负数的补码表示会被解释为一个大的正数。例如,-1 (int) 转换为
unsigned int会得到该类型所能表示的最大值 (所有位均为 1)。这可能导致非预期的逻辑错误或比较结果。
- 显式转换 (强制类型转换):
- 可以通过
(unsigned type) expression的形式进行显式强制类型转换,其行为与隐式转换相同,但代码意图更明确。
- 可以通过
int、float、double 之间的转换
- 上述 3 种类型的机器码并不相同(int 位 32 位有符号整数,用补码表示;float 和 double 分别是 32 位和 64 位浮点数,阶码用移码,尾数用原码表示)
- 上述 3 种类型的表示范围和精度也不相同
- 因此转换过程编译器只能保证数值尽量相等,大多数情况下只是近似值
| 转换类型 | 转换类别 | 特点/注意事项 |
|---|---|---|
float → double | 拓宽转换 (Widening) | - float 的有效位 (24 位) 可被 double (53 位) 完全保留,尾数部分通过在低位补零来填充。- 不会发生精度丢失, float 能表示的任何值,double 都能精确表示。 |
double → float | 窄化转换 (Narrowing) | - 存在精度丢失风险:double 尾数截断或舍入(通常四舍五入到偶数)。- 存在溢出风险:如果 double 值超出 float 范围,结果可能为 +/- Infinity。- 存在下溢风险:如果 double 值太小且超出 float 最小正数,结果可能为 +/- 0.0。 |
double/float → int | 窄化转换 (Narrowing) | - 截断小数部分:总是向零取整(例如 3.7 → 3, -3.7 → -3)。 - 溢出风险:如果浮点数绝对值超出 int 范围,结果未定义(通常为 int 最大/最小值)。- NaN 或 Infinity:转换为 int 也是未定义行为(通常为 int 最大/最小值)。 |
int → float | 拓宽转换 (Widening) | - int 的所有值都在 float 表示范围内。- 可能发生精度丢失:当 int 绝对值 大于 (16777216) 时,int 值可能无法被 float 精确表示,会发生舍入。 |
int → double | 拓宽转换 (Widening) | - int 的所有值都在 double 表示范围内。- 不会发生精度丢失: double 的尾数有效位 (53 位) 远大于 int 的位数 (32 位),所有 int 值都可以被 double 精确表示。 |
