经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
结构体对齐的规则详解及C++代码验证
来源:jb51  时间:2021/8/16 18:51:35  对本文有异议

基本概念

CPU一次能读取多少个字节的数据主要是看数据总线是多少位的,16位CPU一次能读取2个字节,32位CPU一次能读取4个字节,64位CPU一次能读取8个字节。并且不能跨内存区间访问,这句话的意思可以理解为,如果CPU是32位的话,那么可以将整个内存区间每4个字节分为一块(BLOCK),每次读取一个BLOCK的数据。

那么对于下面这个结构体:

  1. struct st {
  2. char c;
  3. int i;
  4. };

如果不进行对齐操作,char 的地址范围0x00000000,int的地址范围为0x00000001----0x00000004,int分布在两个不同的BLOCK上,因此要读取int需要两次操作;如果进行4字节对齐,那么int的地址范围为0x00000004----0x00000007,处在一个BLOCK上,因此只需一次读取操作即可。缺点也显而易见,多占用了3个字节。这也是典型的一种空间换时间的方法吧。

结构体对齐的规则

结构体对齐需要满足以下三条规则,其中系统对齐模数在64位机器上默认为8字节,32位机器上默认为4字节。通过预处理指令#pargma pack(N)可以修改系统模数为N个字节。

1、以结构体第一个元素的地址为起始地址,亦即结构体的起始地址。由上可知,第一个元素的偏移量为0;

2、结构体元素对齐原则:结构体成员的对齐模数为类型大小与系统对齐模数的较小者;结构体成员的偏移量(填充)为对齐模数的整数倍。

3、结构体大小对齐原则:结构体的对齐模数为结构体最大元素与系统对齐模数的较小者;结构体的大小(填充)为结构体对齐模数的整数倍。

程序验证

测试环境为64位Windows ,VS2019,定义结构体st1,包含3个元素char,int,double,定义系统对齐模数为4个字节。

  1. #include <iostream>
  2.  
  3. #pragma pack(4)
  4.  
  5. using Tsize = unsigned long long;
  6. using namespace std;
  7.  
  8. struct st1 {
  9. char c;
  10. int i;
  11. double db;
  12. Tsize ch_offset() {
  13. return Tsize((Tsize)&this->c - (Tsize)this);
  14. }
  15. Tsize int_offset() {
  16. return Tsize((Tsize)&this->i - (Tsize)this);
  17. }
  18. Tsize double_offset() {
  19. return Tsize((Tsize)&this->db - (Tsize)this);
  20. }
  21. };
  22.  
  23. int main() {
  24. cout << "st1结构体大小" << sizeof(st1) << endl;
  25. cout << "char 偏移量=" << st1().ch_offset() << endl;
  26. cout << "int偏移量=" << st1().int_offset() << endl;
  27. cout << "double偏移量=" << st1().double_offset() << endl;
  28. return 0;
  29. }

首先我们按照对齐规则来进行分析。第一个元素为char,类型大小为1个字节,对齐模数min(1,4)=1,偏移量为0是对齐模数的整数倍,无需填充(从这里我们可以看到,第一个字节偏移量始终为0,是不需要填充的),下一个元素的偏移从1开始;第二个元素为int,类型大小4个字节,对齐模数为4个字节,不填充时偏移量为1,不是4的整数倍,因此这里需要填充3个字节,使得int的偏移量为4,且下一个元素偏移从8开始;第三个元素是double,类型大小为8个字节,对齐模数为4,偏移量从8开始,是4的整数倍,因此无需填充,占用8个字节,因此结构体的大小为16个字节。运行程序输出:

在这里插入图片描述

将系统对齐模数修改为1 【#pargma pack(1)】,这样的话,任何情况下都无需填充(不足一个字节的类型视为一个字节),结构体的大小即为结构体元素大小之和。运行程序输出:

在这里插入图片描述

将系统对齐模数修改为8 【#pargma pack(8)】,这样的话,任何情况下都无需填充(不足一个字节的类型视为一个字节),结构体的大小即为结构体元素大小之和。运行程序输出:

在这里插入图片描述

Emmm,好像和系统对齐模数为4时没什么变化。那我们将int 和 double的顺序换一下呢?

  1. #include <iostream>
  2.  
  3. #pragma pack(8)
  4.  
  5. using Tsize = unsigned long long;
  6. using namespace std;
  7.  
  8. struct st1 {
  9. char c;
  10. double db;
  11. int i;
  12. Tsize ch_offset() {
  13. return Tsize((Tsize)&this->c - (Tsize)this);
  14. }
  15. Tsize int_offset() {
  16. return Tsize((Tsize)&this->i - (Tsize)this);
  17. }
  18. Tsize double_offset() {
  19. return Tsize((Tsize)&this->db - (Tsize)this);
  20. }
  21. };
  22.  
  23. int main() {
  24. cout << "st1结构体大小" << sizeof(st1) << endl;
  25. cout << "char 偏移量=" << st1().ch_offset() << endl;
  26. cout << "double偏移量=" << st1().double_offset() << endl;
  27. cout << "int偏移量=" << st1().int_offset() << endl;
  28. return 0;
  29. }

在这里插入图片描述

顺序调了下,结构体就比原来大了8个字节!!!从中我们可以看出,将结构体元素从小到大排列,可以最大程度节省空间。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注w3xue的更多内容!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号