对齐方式 - C++ 结构体字节数为什么和成员字节数总和不等

C++ Builder 参考手册对齐方式


有时候,我们发现 C++ 结构体的字节数并不等于这个结构体所有成员字节数的总和,而是比这个字节数总和要多,例如下面的一段程序:

typedef struct
{
    char   a;
    float  b;
    short  c;
    double d;
}   TMyStruct;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
     ShowMessage(sizeof(TMyStruct));
}

运行结果为 24,即这个结构体的大小为 24 个字节:

运行结果:这个结构体的大小为 24 个字节

也就是 TMyStruct 结构体的大小是 24 个字节,而每个成员的总和只有 15 个字节。

char a; // 1个字节
float b; // 4个字节
short c; // 2个字节
double d; // 8个字节
------→ 一共15个字节

如果希望这个结构体的字节数和这个结构体所有成员的字节数总和相等,需要在这个结构体放在 #pragma pack(push,1) 和 #pragma pack(pop) 之间:

#pragma pack(push,1)
typedef struct
{
    char   a;
    float  b;
    short  c;
    double d;
}   TMyStruct;
#pragma pack(pop)

void __fastcall TForm1::Button1Click(TObject *Sender)
{
     ShowMessage(sizeof(TMyStruct));
}

运行结果为 15,即此时这个结构体的字节数为 15 个字节:

运行结果:这个结构体的大小为 15 个字节

产生这个现象的原因是内存里面的数据对齐方式引起的。
对于 C 语言,最基本的整数或浮点数变量按照字节数区分,有1、2、4、8 和 16 个字节的变量,对于 Intel x86 / x64 CPU,其中:

• 1个字节的变量,放在内存的任何位置读写速度都是一样的;
• 2个字节的变量,从偶数地址读写的速度是最快的;
• 4个字节的变量,从 4 的整数倍地址读写的速度是最快的;
• 8个字节的变量,从 8 的整数倍地址读写的速度是最快的;
• 16个字节的变量,从 16 的整数倍地址读写的速度是最快的。

变量按照定义的顺序放在内存里面,按照访问速度最快的数据对齐方式存储:
char a; // 1个字节,放在地址 0
float b; // 4个字节,放在地址 4 - 7
short c; // 2个字节,放在地址 8 - 9
double d; // 8个字节,放在地址 16 - 23
这样整个结构体的存放就是从地址 0 到 23,一共占用了 24 个字节。

如果把对齐方式改为 n,n = 1, 2, 4, 8 或 16,表示:
字节数≤n 的变量按照他们期望的对齐方式,字节数>n 的变量对齐在 n 的整数倍地址上,如果 n = 1,那么就取消了对齐方式,变量可以放在任何地址上 (因为 1 的整数倍地址就是任何地址):

#pragma pack(push) // 记住目前的对齐方式
#pragma pack(n)    // 把对齐方式改为 n,n = 1, 2, 4, 8 或 16

// 此处的对齐方式为 n

#pragma pack(pop)  // 恢复前面记住的对齐方式

其中 #pragma pack(push) 和 #pragma pack(n) 可以合并为 #pragma pack(push,n)

那么就是这样的:

#pragma pack(push,n) // 记住目前的对齐方式,把对齐方式改为 n,n = 1, 2, 4, 8 或 16

// 此处的对齐方式为 n

#pragma pack(pop)

这样就可以解释以下代码:

#pragma pack(push,1) // 记住目前的对齐方式,把对齐方式改为 1,变量可以放在任何地址上
typedef struct
{
    char   a; // 假定地址为 0
    float  b; // 地址为 1 - 4
    short  c; // 地址为 5 - 6
    double d; // 地址为 7 - 14
}   TMyStruct; // 整个结构体地址 0 - 14 为 15 个字节
#pragma pack(pop) // 恢复前面记住的对齐方式

void __fastcall TForm1::Button1Click(TObject *Sender)
{
     ShowMessage(sizeof(TMyStruct));
}

运行结果是这个结构体的大小为 15,即所有成员的字节数的总和。


C++ Builder 参考手册对齐方式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对...
    saygoodbye_e92e阅读 929评论 0 0
  • @[c++|struct] 今天在编程中碰到一个坑,搞的调试了半天,最后发现程序中在写数据和读取数据时结构体定义不...
    drybeans阅读 3,648评论 1 11
  • 转载 结构体对齐详解 结构体数据成员对齐的意义 许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会...
    erU阅读 494评论 0 3
  • 作者:Eric S. Raymond转自:https://github.com/ludx/The-Lost-Art...
    FlyingReganMian阅读 1,335评论 0 4
  • 技术交流QQ群:1027579432,欢迎你的加入! 1.C++中的字节对齐 字节对齐解释:现代计算机中内存空间都...
    CurryCoder阅读 3,213评论 4 3