sizeof直接获取类总大小,offsetof获取非静态非引用非位域成员的字节偏移;二者要求类为标准布局,虚函数、虚继承、静态成员等会使offsetof失效或结果不可靠。

用 sizeof 和 offsetof 获取类大小与成员偏移
直接用 sizeof 得到类的总大小,用 offsetof(需包含 <cstddef>)获取任意数据成员相对于类起始地址的字节偏移。这是最标准、零开销的方式,但前提是成员必须是**非静态、非引用、非位域**的普通数据成员。
-
offsetof对静态成员、函数、引用类型、位域、虚函数表指针(vptr)都无效——编译会报错或行为未定义 - 类必须是标准布局(
std::is_standard_layout_v<T>为true),否则offsetof结果不可靠(虽然多数编译器仍能工作,但标准不保证) - 如果类有虚函数或虚基类,
sizeof包含 vptr(通常 8 字节,64 位平台),但它在内存中的位置由编译器决定,offsetof无法定位 vptr
打印所有成员偏移的实用写法
手动对每个成员调用 offsetof 并输出,比依赖调试器更可控、可复现。注意:成员声明顺序影响偏移,但编译器可能因对齐插入填充,所以不能简单累加类型大小。
#include <iostream>
#include <cstddef>
struct Example {
char a;
int b;
short c;
};
int main() {
std::cout << "sizeof(Example) = " << sizeof(Example) << "\n";
std::cout << "offsetof(a) = " << offsetof(Example, a) << "\n";
std::cout << "offsetof(b) = " << offsetof(Example, b) << "\n";
std::cout << "offsetof(c) = " << offsetof(Example, c) << "\n";
}
输出可能为:sizeof(Example) = 12offsetof(a) = 0offsetof(b) = 4offsetof(c) = 8
说明 char a 后有 3 字节填充,才能让 int b 按 4 字节对齐。
遇到空基类或虚继承时的大小变化
空基类优化(EBO)会让派生类大小不增加,但一旦引入虚继承,就会额外添加虚基类指针(vbptr),且 offsetof 无法用于虚基类成员——因为其实际偏移在运行时才确定。
- 普通空基类:
sizeof(EmptyBase)是 1,但继承后常被优化掉 - 虚继承:
sizeof(DerivedWithVirtualBase)通常多出一个指针大小(8 字节),且虚基类成员偏移无法用offsetof获取 - 使用
__attribute__((packed))(GCC/Clang)或#pragma pack可禁用填充,但会破坏对齐,影响性能甚至导致崩溃(尤其在 ARM 或 SSE 操作时)
调试时容易忽略的对齐和填充细节
成员偏移不是“前面所有成员大小之和”,而是受当前最大对齐要求支配。例如 double d 成员(对齐要求 8)出现在 char 和 int 后,前面很可能插入 4 字节填充,哪怕 sizeof 看起来“浪费”了空间。
立即学习“C++免费学习笔记(深入)”;
- 用
alignof(T)查看类型的对齐要求,它直接影响填充位置和数量 - 结构体整体对齐等于其最大成员的对齐值,这也会导致末尾填充(比如 12 字节结构体含
double,sizeof可能是 16) - 不同编译器或平台(x86 vs ARM64)可能有细微差异,别假设偏移跨平台一致
偏移和大小最终取决于 ABI 和编译器实现,不要硬编码这些值做序列化或跨进程共享——用 static_assert 校验关键偏移更稳妥。



















