之前使用结构体不是很多, 了解不多, 最近使用结构体遇到数据对齐问题, 于是决定把这个结构体的数据对齐问题摸透, 随便收录入我的博客
作为一种数据集合, struct 常用在数据结构中而 struct 的字节对齐方式对于嵌入式底层的程序员来讲是必须掌握的
现代计算机中内存空间都是按照 byte 划分的, 从理论上讲似乎对任何类型的变量的访问可以从任何地址开始, 但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问, 这就需要各种类型数据按照一定的规则在空间上排列, 而不是顺序的一个接一个的排放, 这就是对齐对齐的作用和原因: 各个硬件平台对存储空间的处理上有很大的不同一些平台对某些特定类型的数据只能从某些特定地址开始存取比如有些架构的 CPU 在访问 一个没有进行对齐的变量的时候会发生错误, 那么在这种架构下编程必须保证字节对齐. 其他平台可能没有这种情况, 但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐, 会在存取效率上带来损失比如有些平台每次读都是从偶地址开始, 如果一个 int 型 (假设为 32 位系统) 如果存放在偶地址开始的地方, 那么一个读周期就可以读出这 32bit, 而如果存放在奇地址开始的地方, 就需要 2 个读周期, 并对两次读出的结果的高低字节进行拼凑才能得到该 32bit 数 据显然在读取效率上下降很多
首先我们在 VC++6.0 写个小程序测试下,
小编推荐一个学 C 语言 / C++ 的学习裙 六九九, 四七零, 五九六 , 无论你是大牛还是小白, 是想转行还是想入行都可以来了解一起进步一起学习! 裙内有开发工具, 很多干货和技术资料分享!
程序运行后的结果:
- Sizeof(A)=8
- Sizeof(B)=12
- Press any key to continue
结构体 A 中包含了 4 字节长度的 int 一个, 1 字节长度的 char 一个和 2 字节长度的 short 型数据一个, B 也一样; 按理说 A,B 大小应该都是 7 字节之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐以上是按默认方式对齐
默认对齐: 各成员变量在存放的时候根据在结构中出现的顺序依次申请空间, 同时按照上面的对齐方式调整位置, 空缺的字节 VC 会自动填充同时 VC 为了确保结构的大小为结构的字节边界数 (即该结构中占用最大空间的类型所占用的字节数) 的倍数, 所以在为最后一个成员变量申请空间后, 还会根据需要自动填充空缺的字节
默认对齐方式的过程:
结构 A 分配空间的时候, 先为第一个成员 a 分配空间, 其起始地址跟结构的起始地址相同 (刚好偏移量 0 刚好为 sizeof(int) 的倍数), 该成员变量占用 sizeof(int)=4 个字节; 接下来为第二个成员 b 分配空间, 这时下一个可以分配的地址对于结构的起始地址的偏移量为 4, 是 sizeof(char)的倍数, 所以把成员 b 存放在偏移量为 4 的地方满足对齐方式, 该成员变量占用 sizeof(char)=1 个字节; 接下来为第三个成员 c 分配空间, 这时下一个可以分配的地址对于结构的起始地址的偏移量为 5, 不是 sizeof(short)=2 的倍数, 为了满足对齐方式对偏移量的约束问题, VC 自动填充 1 个字节 (这一个字节没有放什么东西), 这时下一个可以分配的地址对于结构的起始地址的偏移量为 6, 刚好是 sizeof(short)=2 的倍数, 所以把 c 存放在偏移量为 6 的地方, 该成员变量占用 sizeof(short)=2 个字节; 这时整个结构的成员变量已经都分配了空间, 总的占用的空间大小为: 4+1+1+2=8, 刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数 sizeof(int)=4) 的倍数, 所以没有空缺的字节需要填充所以整个结构的大小为: sizeof(A)=4+1+1+2=8, 其中有 1 个字节是 VC 自动填充的, 没有放任何有意义的东西
结构 B 分配空间的时候, 先为第一个成员 a 分配空间, 其起始地址跟结构的起始地址相同 (刚好偏移量 0 刚好为 sizeof(char) 的倍数), 该成员变量占用 sizeof(char)=1 个字节; 接下来为第二个成员 b 分配空间, 这时下一个可以分配的地址对于结构的起始地址的偏移量为 1, 不是 sizeof(int)的倍数, 为了满足对齐方式对偏移量的约束问题, VC 自动填充 3 个字节 (这三个字节没有放什么东西), 这时下一个可以分配的地址对于结构的起始地址的偏移量为 4, 刚好是 sizeof(int)=4 的倍数所以把成员 b 存放在偏移量为 4 的地方满足对齐方式, 该成员变量占用 sizeof(int)=4 个字节; 接下来为第三个成员 c 分配空间, 这时下一个可以分配的地址对于结构的起始地址的偏移量为 8, 刚好是 sizeof(short)=2 的倍数, 所以把 c 存放在偏移量为 8 的地方, 该成员变量占用 sizeof(short)=2 个字节; 这时整个结构的成员变量已经都分配了空间, 总的占用的空间大小为: 1+3+4+2=10, 不是结构的字节边界数(即结构中占用最大空间的类型所占用的字节数 sizeof(int)=4) 的倍数, 所以要填补 2 个空缺的字节所以整个结构的大小为: sizeof(B)=1+3+4+2+2=12, 其中有 5 个字节是 VC 自动填充的, 没有放任何有意义的东西
VC 中提供了 #pragmapack(n)来设定变量以 n 字节对齐方式 n 字节对齐就是说变量存放的起始地址的偏移量有两种情况: 第一如果 n 大于等于该变量所占用的字节数, 那么偏移量必须满足默认的对齐方式, 第二如果 n 小于该变量的类型所占用的字节数, 那么偏移量为 n 的倍数, 不用满足默认的对齐方式结构的总大小也有个约束条件, 分下面两种情况: 如果 n 大于所有成员变量类型所占用的字节数, 那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;
否则必须为 n 的倍数下面举例说明其用法
- #pragmapack(push) // 保存对齐状态
- #pragmapack(4)// 设定为 4 字节对齐
小编推荐一个学 C 语言 / C++ 的学习裙 六九九, 四七零, 五九六 , 无论你是大牛还是小白, 是想转行还是想入行都可以来了解一起进步一起学习! 裙内有开发工具, 很多干货和技术资料分享!
#pragmapack(pop)// 恢复对齐状态以上结构的大小为 16, 下面分析其存储情况, 首先为 m1 分配空间, 其偏移量为 0, 满足我们自己设定的对齐方式 (4 字节对齐),m1 占用 1 个字节接着开始为 m4 分配空间, 这时其偏移量为 1, 需要补足 3 个字节, 这样使偏移量满足为 n=4 的倍数(因为 sizeof(double) 大于 n),m4 占用 8 个字节接着为 m3 分配空间, 这时其偏移量为 12, 满足为 4 的倍数, m3 占用 4 个字节这时已经为所有成员变量分配了空间, 共分配了 16 个字节, 满足为 n 的倍数如果把上面的 #pragmapack(4)改为 #pragma pack(8), 那么我们可以得到结构的大小为 24
来源: http://www.jianshu.com/p/bc1eeff85862