sizeof 是 C/C++ 中的一元运算符,不是函数。它用来求某个类型或对象在内存中占用的字节数,结果类型是 size_t(无符号整数,定义在 <stddef.h> / <cstddef>)。
1. 基本语法
sizeof 表达式
sizeof(类型名)
例如:
sizeof(int)
sizeof(char)
sizeof a
sizeof(a)
sizeof(struct Student)
注意:
- 对类型使用时,括号通常是必须的:
sizeof(int)。 - 对表达式使用时,括号可省略:
sizeof a和sizeof(a)等价。 - 括号不代表函数调用,
sizeof是运算符。
C++11 还有参数包版本:
sizeof...(Args) // 返回参数包中元素个数,不是字节数
2. 关键性质
2.1 单位是“字节”,但标准中的字节是 char 的大小
C 标准规定:
sizeof(char) == 1
这里的“1 字节”是 char 所占的存储单元。通常一个字节是 8 位,但不绝对,具体由 CHAR_BIT 决定。
2.2 通常是编译期常量
在 C++ 标准中,sizeof 是编译期常量表达式,可用于:
static_assert(sizeof(int) == 4);
int arr[sizeof(int)];
在 C 中,除变长数组 VLA 外,sizeof 也是编译期常量。
2.3 一般不评估操作数
int i = 0;
size_t n = sizeof(i++);
printf("%d\n", i); // 仍然是 0
因为 sizeof 只关心 i++ 的类型,不会真正执行 i++。
例外:C99 的 VLA,如果操作数类型是变长数组,sizeof 可能在运行时计算。
2.4 返回类型是 size_t
打印时建议:
printf("%zu\n", sizeof(int)); // C99/C++11
C++ 中:
std::cout << sizeof(int) << '\n';
不要随便用 %d,因为 size_t 是无符号类型,宽度也可能不同。
3. 对数组和指针的区别
这是最经典的陷阱。
int a[10];
sizeof(a); // 10 * sizeof(int),通常是 40
sizeof(a[0]); // sizeof(int),通常是 4
sizeof(a + 0); // sizeof(int*),指针大小,通常是 8
关键点:
a是数组名,在sizeof中不会退化为指针,所以得到整个数组大小。a + 0是表达式,数组名退化为指针,所以得到指针大小。- 数组做函数参数时会退化为指针:
void f(int a[10]) {
printf("%zu\n", sizeof(a)); // 指针大小,不是 40
}
如果真想传数组并保留大小,可以用:
void f(int (*a)[10]) {
printf("%zu\n", sizeof(*a)); // 整个数组大小
}
C++ 中可写:
template <size_t N>
void f(int (&a)[N]) {
std::cout << sizeof(a) << '\n'; // N * sizeof(int)
}
4. 字符串字面量
char s[] = "hello";
char *p = "hello";
sizeof(s); // 6,包含结尾 '\0'
strlen(s); // 5,不包含 '\0'
sizeof(p); // 指针大小
strlen(p); // 5
sizeof 是求内存占用,strlen 是运行时求字符串长度。
5. 结构体、对齐和填充
sizeof(struct) 不一定等于成员大小之和,因为存在内存对齐和填充。
struct A {
char c;
int i;
};
典型 64 位平台上:
c在偏移 0;- 为了
int对齐,偏移 1~3 填充; i在偏移 4;- 总大小通常为 8。
struct B {
int i;
char c;
};
通常也是 8,因为结构体总大小要按最大对齐数补齐。
常见规则:
- 每个成员按自身对齐要求放在合适偏移;
- 结构体总大小通常是最大成员对齐数的整数倍;
- 可用
#pragma pack改变对齐,但可能降低访问效率; - 可用
offsetof查成员偏移,用alignof/_Alignof查对齐。
位域不能直接 sizeof:
struct BF {
unsigned a : 1;
unsigned b : 2;
};
// sizeof(BF.a); // 错误
但 sizeof(struct BF) 是合法的,表示整个位域结构体大小。
6. C++ 中的类对象大小
C++ 中 sizeof 对类类型返回对象本身大小,不包括动态分配内存。
struct Empty {};
sizeof(Empty); // 通常为 1
空类大小不是 0,因为每个对象必须有唯一地址。
struct A {
int x;
static int y;
void f();
};
sizeof(A) 通常等于 sizeof(int)。
静态成员、成员函数不占对象大小。
有虚函数时,通常会多一个虚表指针:
struct B {
virtual void f();
};
64 位平台上 sizeof(B) 通常为 8,即一个指针大小。
struct C {
virtual void f();
int x;
};
64 位平台上通常为 16:8 字节 vptr + 4 字节 int + 4 字节填充。
C++ 还有空基类优化:
struct Empty {};
struct D : Empty {
int x;
};
sizeof(D); // 通常等于 sizeof(int)
引用:
int x = 0;
int& r = x;
sizeof(r); // sizeof(int)
sizeof(int&); // 通常也等于 sizeof(int)
sizeof 对引用得到的是被引用类型的大小。
7. C 和 C++ 的一些差异
字符常量
sizeof('a');
- C 中字符常量类型是
int,所以sizeof('a') == sizeof(int),通常为 4。 - C++ 中字符常量类型是
char,所以sizeof('a') == 1。
空结构体/空类
- C++:空类
sizeof通常为 1。 - C:空结构体不是标准用法,GCC 扩展下可能为 0。
VLA
- C99 支持变长数组,
sizeof可能在运行时计算。 - C++ 标准不支持 VLA,只是部分编译器扩展。
sizeof(void)
标准 C/C++ 都不允许。
GCC 等扩展中 sizeof(void) == 1,但不要依赖。
8. 常见非法用法
struct Node;
sizeof(struct Node); // 错误:不完整类型
sizeof(main); // 错误:函数类型
struct BF { unsigned a : 1; };
sizeof(BF.a); // 错误:位域
函数指针可以:
sizeof(&main); // 指针大小
9. 常见陷阱总结
-
数组名在
sizeof中不退化为指针,但作为函数参数会退化。 -
sizeof(指针)得到指针大小,不是它指向的内存大小。 -
malloc后:int *p = malloc(100 * sizeof(int)); sizeof(p); // 指针大小,不是 400 -
sizeof包含结构体填充,不等于成员大小简单相加。 -
sizeof不包含动态分配内存,只算对象本身。 -
sizeof结果是无符号size_t,和负数比较容易出问题。 -
平台相关:
int、long、指针大小不一定相同。
10. 一句话记忆
sizeof 是运算符,不是函数;它求类型或对象占用的字节数,通常编译期确定,不计算表达式,结果类型是 size_t。对数组得整个数组大小,对指针得指针大小,对结构体/类包含对齐填充,但不包含动态分配内容。