C 基本数据类型
更新时间:2026-08-26。本文是
languages/c/主题入门层第 5 篇(由原《数据类型与内存表示》拆分重组)。类型是 C 的基石——它决定了变量占几字节、能存多大数、运算时如何解释。搞懂类型,才能理解后面的指针、结构体对齐,以及性能剖析时看到的每个字节。
本文要回答的问题
int、long、size_t各占几字节?为什么不能想当然?float和double差在哪?为什么 0.1 + 0.2 不等于 0.3?- 为什么
sizeof返回size_t,打印它要用%zu而不是%d?
一、基本类型全景
C 语言的基本类型分四大族:
| 族 | 关键字 | 用途 |
|---|---|---|
| 整型 | char short int long long long | 存整数 |
| 浮点 | float double long double | 存小数 |
| 字符 | char(本质是 1 字节整型) | 存字符 |
| 无符号 | unsigned 前缀 | 存非负整数,范围翻倍 |
注意 char 很特殊:它既是字符类型也是最小的整型,本质就是"1 字节的数"。字符 'A' 存的就是 65(ASCII 码),你完全可以拿它当数字算。
二、整型的字节数与取值范围
关键认知:C 标准只规定"最小范围",不规定"确切字节数"。确切字节数由平台(LP64 / LLP64)决定:
| 类型 | 最小位数 | 典型字节(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,来自<stdint.h>)。
自己验证一段:
#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。建议你在自己机器上跑一遍——"我的 long 是几字节"这个问题,跑一次 sizeof 比背十遍文档都牢。
三、浮点类型:精度是最大的坑
| 类型 | 字节 | 有效位数 | 用途 |
|---|---|---|---|
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(二进制里 0.1 是无限循环小数,只能近似存储)。算钱别用浮点。 - 不要用
==比较浮点:两个"应该相等"的浮点数可能差一点点。比较用误差范围:fabs(a - b) < 1e-9(fabs来自<math.h>)。
另外记住 int 到 float/double 的转换会损失精度:double 只有约 15 位有效数字,超过的部分是噪声。
四、size_t 与 sizeof
4.1 size_t 是什么
sizeof 的返回类型,一个无符号整型,专门用来表示"大小 / 长度"。它设计得足够大,能装下任意对象的大小:
size_t n = sizeof(int); // n = 4
size_t len = strlen("abc"); // len = 3(strlen 也返回 size_t)strlen、sizeof、malloc 的参数、数组下标——凡是涉及"大小"的地方,标准库都用 size_t。
4.2 打印 size_t 用 %zu
size_t n = 10;
printf("%zu\n", n); // 正确
// printf("%d\n", n); // 错误:类型不匹配,-Wall 会警告%zu 里的 z 是"对应 size_t 的长度修饰符",u 是无符号十进制。用 %d 打印 size_t 是经典的类型不匹配——小数字碰巧能打对,一超过 int 范围就露馅,还可能打出乱码。格式串和参数类型必须匹配,这条规则后面还会反复遇到。
五、固定宽度类型:跨平台的答案
觉得 int/long 大小不固定很烦?C99 提供了固定宽度整型(<stdint.h>):
#include <stdint.h>
int32_t i = 42; // 一定有符号、恰好 4 字节
uint64_t u = 42; // 无符号、恰好 8 字节它们的打印格式也固定:int32_t 用 PRId32(平台相关的宏,自动展开成正确的格式符):
printf("%" PRId32 "\n", i);写网络协议、二进制格式、需要精确控制内存布局的场景,用固定宽度类型是最稳的选择。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
假设 long 是 4 字节 | 跨平台后数据错位 | 用 sizeof 实测或用 int32_t |
%d 打印 size_t | 大数输出错乱 | 用 %zu |
== 比较浮点 | 明明相等却判断失败 | 用 fabs(a - b) < 1e-9 |
| 整数除法当浮点除 | 5 / 2 等于 2 | 先转 double 再除(见类型转换) |
float 存精确小数 | 账目对不上 | 算钱用整数(分) |
七、与本站主线衔接
- 内存表示:每个类型的字节数决定了
perf火焰图里访存指令操作的数据宽度,见 L3 内存子系统; - 对齐:类型大小与结构体对齐直接相关,见高手层 结构体内存对齐;
- 有符号 / 无符号的溢出与隐式转换坑,见下一篇 有符号与无符号。
一句话总结
C 的类型决定变量占几字节、能存多大数——long 的大小因平台而异、浮点用 double 且别用 == 比较、size_t 用 %zu 打印;搞懂类型与字节,是理解指针、对齐、性能数据的前提。