C 语言数据类型与内存表示
更新时间:2026-08-25。本文是
languages/c/主题入门层第 1 篇。类型是 C 的基石——它决定了变量占几字节、能存多大数、运算时如何解释。搞懂类型,才能理解后面的指针、结构体对齐、以及性能剖析时看到的每个字节。
本文要回答的问题
int、long、size_t各占几字节?为什么不能想当然?- 有符号和无符号混用时,会掉进什么坑?
- 为什么
sizeof返回size_t,打印它要用%zu而不是%d?
一、基本类型全景
C 语言的数据类型分四大族,本文聚焦基本类型:
| 族 | 关键字 | 用途 |
|---|---|---|
| 整型 | char short int long long long | 存整数 |
| 浮点 | float double long double | 存小数 |
| 字符 | char(本质是 1 字节整型) | 存字符 |
| 无符号 | unsigned 前缀 | 存非负整数,范围翻倍 |
二、整型的字节数与取值范围
关键认知:C 标准只规定"最小范围",不规定"确切字节数"。确切字节数由平台(LP64/LP32)决定:
| 类型 | 最小位数 | 典型字节(64 位 Linux) | 取值范围(典型) |
|---|---|---|---|
char | 8 | 1 | -128 ~ 127 |
short | 16 | 2 | -32768 ~ 32767 |
int | 16 | 4 | -2^31 ~ 2^31-1 |
long | 32 | 8(LP64) | -2^63 ~ 2^63-1 |
long long | 64 | 8 | -2^63 ~ 2^63-1 |
unsigned int | 16 | 4 | 0 ~ 2^32-1 |
注意
long的平台差异:Windows(LLP64)上long是 4 字节,Linux(LP64)上是 8 字节。写跨平台代码时别假设long的大小——用sizeof或固定宽度类型(int32_t/int64_t)。
验证一段:
#include <stdio.h>
int main(void) {
printf("char : %zu\n", sizeof(char));
printf("short : %zu\n", sizeof(short));
printf("int : %zu\n", sizeof(int));
printf("long : %zu\n", sizeof(long));
printf("size_t : %zu\n", sizeof(size_t));
return 0;
}在 64 位 Linux 上输出:char=1, short=2, int=4, long=8, size_t=8。
三、有符号 vs 无符号:最容易踩的坑
3.1 无符号的"溢出回绕"
unsigned int u = 0;
u = u - 1; // 回绕到 4294967295(2^32-1)无符号数运算结果超出范围时会回绕(模运算),这是定义好的行为,不会报错。
3.2 有符号溢出是未定义行为
int i = 2147483647; // INT_MAX
i = i + 1; // 未定义行为!可能变成负数,也可能被优化器假设"不会发生"有符号溢出是 UB(未定义行为)——编译器在 -O2 下会基于"不会溢出"的假设做激进优化,导致诡异 bug。
3.3 有符号与无符号混用:隐式转换陷阱
int a = -1;
unsigned int b = 1;
if (a < b) { // 结果是什么?
printf("a < b\n");
} else {
printf("a > b\n"); // 实际打印这个!
}原因是:a(有符号)与 b(无符号)比较时,a 会被隐式转换成无符号,-1 变成 4294967295,于是 a > b。
预防:比较时显式转换,或避免有符号/无符号混用;编译时加 -Wall -Wextra 能警告这类问题。
四、浮点类型
| 类型 | 字节 | 有效位数 | 用途 |
|---|---|---|---|
float | 4 | ~7 位十进制 | 精度要求低 |
double | 8 | ~15 位十进制 | 默认选择 |
long double | 16(x86-64) | ~19 位 | 高精度 |
float f = 3.14f; // 字面量加 f 表示 float,否则是 double
double d = 3.14;两个浮点坑:
- 不能精确表示:
0.1 + 0.2 != 0.3,因为二进制浮点无法精确表示大部分十进制小数。 - 不要用
==比较浮点:用误差范围fabs(a - b) < 1e-9。
五、size_t 与 sizeof
5.1 size_t 是什么
sizeof 的返回类型,是无符号整型,用来表示"大小/长度"。它足够大,能表示任意对象的大小。
size_t n = sizeof(int); // n = 4
size_t len = strlen("abc"); // len = 35.2 打印 size_t 用 %zu
size_t n = 10;
printf("%zu\n", n); // 正确
// printf("%d\n", n); // 错误:类型不匹配,-Wall 会警告%zu 里的 z 表示"对应 size_t 的长度修饰符",u 表示无符号十进制。
5.3 size_t 是无符号的——小心循环
for (size_t i = 10; i >= 0; i--) { // 死循环!
// i 是无符号,i >= 0 永远为真,减到 0 后回绕成巨大数
}这是经典陷阱:无符号的 i >= 0 恒真,循环退不出。
六、类型转换
6.1 隐式转换(自动)
小类型向大类型"提升":char/short → int,int → long,int → double 等。这是算术转换规则。
6.2 显式转换(强制)
int a = 5, b = 2;
double r = a / b; // r = 2.0!先整数除法,再转 double
double r2 = (double)a / b; // r2 = 2.5,正确整数除法会截断,要先转成浮点再除。
七、与本站主线衔接
- 内存表示:每个类型的字节数决定了
perf火焰图里那些访存指令操作的数据宽度,见 L3 内存子系统。 - 对齐:类型大小与结构体对齐直接相关,见 L3 对齐与 TLB。
- 溢出与 UB:未定义行为是编译器优化的前提,见 L4 编译器选项。
一句话总结
C 的类型决定了变量占几字节、存多大数——long 的大小因平台而异、有符号/无符号混用会隐式转换、size_t 要用 %zu 打印;搞懂类型与字节,是理解指针、对齐、性能数据的前提。
上一篇:C 语言三层学习路径总纲 下一篇:控制流与函数