01 / 80 · C++11 · 约 7 分钟
sizeof:测量对象表示,而非运行时内容
sizeof 查询静态类型的对象表示大小,结果以 C++ 字节为单位,包含必要的填充。它通常看起来像函数调用,实际是操作数不求值的运算符;数组、引用与指针的结果必须分别理解。
先确定测量单位
sizeof(T) 返回一个完整 T 对象的对象表示所占字节数,结果类型是 std::size_t。这里的字节不是固定八位:sizeof(char) 必为 1,而每字节的位数由 CHAR_BIT 给出,至少为 8。不要把某台机器的 int 为四字节、指针为八字节写成语言保证。
结构体的结果包含成员之间和末尾的必要填充,因此不一定等于各成员大小之和。空类的完整对象也有非零大小;这不意味着空基类子对象一定额外占一个字节。回答大小问题时,先说明标准保证,再说明目标 ABI 的具体布局。
不求值仍然检查类型
sizeof(++n) 不会增加 n,因为操作数属于不求值语境。编译器仍然要做名字查找和类型检查;未声明的名字、函数类型、不完整类型以及直接对位域求大小都不能因此变合法。标准 C++ 不提供 C 风格的变长数组规则。
对表达式求大小看的是静态类型,而不是运行时对象的动态类型。通过基类引用查看派生对象,得到的是基类大小。对引用类型本身使用 sizeof,得到被引用类型的大小,不能据此判断实现内部如何存放引用。
数组保留边界,指针不带容量
sizeof 的操作数不会发生数组到指针转换,所以局部数组 a 的 sizeof(a) / sizeof(a[0]) 能得到元素数。示例同时证明数组大小关系和不求值行为,不打印任何依赖平台的类型大小。
指针的 sizeof 只测量指针对象,不能得知它指向一个元素、数组还是动态分配的存储。实际接口应让长度随数据传递,例如使用容器的 size,或在 C++20 中接收 span,而不是尝试从指针恢复容量。
容易答错的地方
- sizeof 的类型形式需要括号;sizeof a + b 的含义是 (sizeof a) + b,不是 sizeof(a + b)。
- sizeof(std::string) 只反映字符串对象本身,不包含它可能在动态存储中拥有的全部字符空间。
运行一个例子
最低标准 C++11 · 完整程序 · 下载 .cpp
#include <cassert>
#include <cstddef>
#include <iostream>
int main() {
int n = 4;
int values[3] = {1, 2, 3};
const std::size_t measured = sizeof(++n);
static_assert(sizeof(char) == 1, "C++ byte");
static_assert(sizeof(values) == 3 * sizeof(int), "array size");
static_assert(sizeof(int&) == sizeof(int), "reference target");
assert(n == 4);
assert(measured == sizeof(int));
std::cout << n << ' ' << sizeof(values) / sizeof(values[0]) << '\n';
}
在本地编译
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-sizeof.cpp -o example && ./example预期结果
4 3
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
设 int a[7]{}; int* p = a; int& r = a[0];,哪些 sizeof 结果能确定为 sizeof(int) 的倍数,能否用 sizeof(p) 求数组长度?
查看参考答案
sizeof(a) 必为 7 * sizeof(int),sizeof(r) 必为 sizeof(int)。sizeof(p) 是 int* 的大小,与数组有七个元素无关。应在数组类型仍然可见的位置取元素数,或显式携带长度;不能用指针大小推断分配大小。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。