sizeof 是 C 语言中一个非常重要的单目运算符,而不是一个函数,它的作用是返回一个对象或类型所占用的内存字节数。

sizeof 的基本语法和用途
sizeof 有两种主要的使用形式:
sizeof (类型名)
这种形式用于获取某个类型在内存中占用的字节数。
printf("int 的大小: %zu 字节\n", sizeof(int));
printf("char 的大小: %zu 字节\n", sizeof(char));
printf("double 的大小: %zu 字节\n", sizeof(double));
printf("指针的大小: %zu 字节\n", sizeof(int*)); // 指针的大小取决于系统架构(32位或64位)
注意:
- 在
printf中,%zu是用于打印size_t类型(sizeof返回的类型)的正确格式说明符,在一些旧的编译器上可能不支持,可以使用%lu并强制转换为unsigned long,但printf("%zu", ...)是现代 C 标准(C99 及以后)推荐的方式。
sizeof 表达式
这种形式用于获取某个变量、数组或结构体等表达式所占用的内存字节数,括号 在对变量使用时是可选的,但加上是更安全、更清晰的做法。

int a = 10;
printf("变量 a 的大小: %zu 字节\n", sizeof(a)); // 或者 sizeof(a)
printf("变量 a 的大小: %zu 字节\n", sizeof a); // 语法正确,但不推荐
int arr[10];
printf("数组 arr 的大小: %zu 字节\n", sizeof(arr)); // 返回整个数组的大小 (10 * sizeof(int))
sizeof 的工作原理:编译时计算
sizeof 的一个关键特性是:它的值在编译期间就已经确定了,而不是在运行时计算的。
这意味着:
sizeof不会导致你的程序真正去访问内存、创建对象或执行任何运行时操作。- 编译器在编译代码时,会根据变量的类型和声明,直接计算出
sizeof的结果,并用这个常量值替换掉sizeof的调用。
示例:
int arr[10];
int size = sizeof(arr); // 编译器直接计算出 10 * 4 = 40 (假设 int 是 4 字节)
// 然后生成 movl $40, -4(%rbp) 这样的汇编指令
// 而不是在运行时循环遍历数组来计算大小。
sizeof 对不同数据对象的应用
a. 基本数据类型
大小取决于编译器和操作系统架构(通常是 32 位或 64 位)。

char: 1 字节short: 2 字节int: 4 字节long: 在 32 位系统上是 4 字节,在 64 位系统上是 8 字节(不绝对,需查阅编译器文档)long long: 8 字节float: 4 字节double: 8 字节long double: 8 或 16 字节- 指针 (): 在 32 位系统上是 4 字节,在 64 位系统上是 8 字节。
b. 数组
sizeof 返回的是整个数组在内存中占用的总字节数,即 元素个数 * 单个元素的大小。
int arr[5];
printf("%zu\n", sizeof(arr)); // 输出 20 (int 是 4 字节)
一个非常重要的陷阱:数组作为函数参数
当数组作为函数参数传递时,它会退化为指向其第一个元素的指针,在函数内部使用 sizeof 得到的不再是数组的大小,而是指针的大小。
#include <stdio.h>
void print_array_size(int arr[]) { // int arr[] 等价于 int *arr
printf("函数内部 arr 的大小: %zu\n", sizeof(arr)); // 输出的是指针的大小 (8)
}
int main() {
int arr[10];
printf("函数外部 arr 的大小: %zu\n", sizeof(arr)); // 输出 40 (10 * 4)
print_array_size(arr);
return 0;
}
如果需要在函数内部知道数组的大小,必须额外将数组的大小作为参数传递进去。
c. 结构体
sizeof 返回的是结构体的总大小,这个大小可能会因为内存对齐的原因,大于所有成员大小之和。
struct MyStruct {
char c; // 1 字节
int i; // 4 字节
double d; // 8 字节
};
printf("%zu\n", sizeof(struct MyStruct)); // 输出可能是 24,而不是 1+4+8=13
内存对齐: 为了让 CPU 能更高效地访问数据,编译器会在结构体成员之间填充一些字节,使得每个成员的起始地址都能被其自身大小的整数倍整除。
char c占 1 字节,从偏移量 0 开始。int i需要 4 字节对齐,所以编译器会在char c后面填充 3 个字节,int i从偏移量 4 开始。double d需要 8 字节对齐,它从偏移量 8 开始。- 结构体总大小也需要是最大成员(这里是
double,8 字节)的整数倍,当前总大小是 1+3+4+8 = 16,已经是 8 的倍数,sizeof(struct MyStruct)是 16。
d. 联合体
联合体的大小是其最大成员的大小,因为所有成员共享同一块内存。
union MyUnion {
char c;
int i;
double d;
};
printf("%zu\n", sizeof(union MyUnion)); // 输出 8,因为 double 是最大的成员
e. 指针
sizeof 返回的是指针本身所占用的字节数,而不是指针指向的内容的大小。
int a = 10;
int *p = &a;
printf("%zu\n", sizeof(p)); // 输出指针的大小 (8)
printf("%zu\n", sizeof(*p)); // 输出指针指向的内容的大小,即 sizeof(int) (4)
sizeof 的返回类型:size_t
sizeof 的返回类型是 size_t,这是一个在 <stddef.h> 中定义的无符号整数类型。
- 为什么是无符号的? 因为大小不可能是负数。
- 为什么是
size_t而不是int?int的大小可能不足以表示一个非常大的内存块(一个 4GB 的数组,sizeof返回 4294967296,这在 32 位int上会溢出)。size_t的大小足够大,可以表示平台上任何对象的大小。
特殊情况
a. sizeof("字符串字面量")
在 C 语言中,字符串字面量(如 "hello")会被存储在内存的只读数据区,并且编译器会自动在末尾添加一个 '\0' (空字符) 作为结束符。
sizeof("hello") 计算的是字符串内容加上结束符所占的总字节数。
printf("%zu\n", sizeof("hello")); // 输出 6 (h, e, l, l, o, \0)
b. sizeof 和 void
sizeof(void) 在 C 标准中是无效的,会引发编译错误,因为 void 表示“无类型”,无法计算其大小。
总结表格
| 对象 | sizeof 行为 |
示例 | 输出 (假设 64位系统, int=4) |
|---|---|---|---|
| 基本类型 | 返回类型的大小 | sizeof(int) |
4 |
| 变量 | 返回变量类型的大小 | int a; sizeof(a) |
4 |
| 数组 | 返回整个数组的总大小 | int arr[10]; sizeof(arr) |
40 |
| 指针 | 返回指针本身的大小 | int *p; sizeof(p) |
8 |
| 结构体 | 返回结构体的总大小(考虑对齐) | struct S {...}; sizeof(S) |
(根据对齐规则计算) |
| 联合体 | 返回最大成员的大小 | union U {...}; sizeof(U) |
(最大成员的大小) |
| 字符串字面量 | 返回字符串长度 + 1 (含 \0) |
sizeof("hello") |
6 |
| 函数参数中的数组 | 退化为指针,返回指针的大小 | void f(int a[]) { sizeof(a); } |
8 |
最佳实践
- 始终使用
size_t:存储sizeof的结果时,使用size_t类型。size_t size = sizeof(my_array);
- 对变量使用时加括号:虽然
sizeof var是合法的,但sizeof(var)更清晰,并且在处理类似sizeof(type)的宏时能避免错误。 - 警惕数组参数退化:在函数中计算数组大小时,
sizeof不可靠,必须手动传递数组大小。 - 理解内存对齐:在处理结构体和内存布局时,
sizeof的结果可能会让你感到意外,理解对齐原理至关重要。
