1分钟了解C语言正确使用字节对齐及#pragma pack的方法

打印 上一主题 下一主题

主题 878|帖子 878|积分 2636

​  C/C++编译器的缺省字节对齐方式为自然对界。即在缺省情况下,编译器为每一个变量或是数据单元按其自然对界条件分配空间。  在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储(成员之间可能有插入的空字节),第一个成员的地址和整个结构的地址相同。
  编译器缺省的结构成员自然对界条件为“N字节对齐”,N即该成员数据类型的长度。如int型成员的自然对界条件为4字节对齐,而double类型的结构成员的自然对界条件为8字节对齐。若该成员的起始偏移不位于该成员的“默认自然对界条件”上,则在前一个节面后面添加适当个数的空字节。
        编译器缺省的结构整体的自然对界条件为:该结构所有成员中要求的最大自然对界条件。若结构体各成员长度之和不为“结构整体自然对界条件的整数倍”,则在最后一个成员后填充空字节。
        例子1(分析结构各成员的默认字节对界条界条件和结构整体的默认字节对界条件):
  1. 1 struct Test
  2. 2 {
  3. 3   char x1; // 成员x1为char型(其起始地址必须1字节对界),其偏移地址为0
  4. 4   char x2; // 成员x2为char型(其起始地址必须1字节对界,其偏移地址为1
  5. 5   float x3; // 成员x3为float型(其起始地址必须4字节对界),编译器在x2和x3之间填充了两个空字节,其偏移地址为4
  6. 6   char x4; // 成员x4为char型(其起始地址必须1字节对界),其偏移地址为8
  7. 7 };
复制代码

        在Test结构体中,最大的成员为float x3,因此结构体的自然对界条件为4字节对齐。则结构体长度就为12字节,内存布局为1100 1111 1000。
  1. 1 #include <stdio.h>
  2. 2 typedef struct
  3. 3 {
  4. 4   int aa1; //4个字节对齐 1111
  5. 5   char bb1;//1个字节对齐 1
  6. 6   short cc1;//2个字节对齐 011
  7. 7   char dd1; //1个字节对齐 1
  8. 8 } testlength1;
  9. 9 int length1 = sizeof(testlength1); //4个字节对齐,占用字节1111 1011 1000,length = 12
  10. 10
  11. 11 typedef struct
  12. 12 {
  13. 13   char bb2;//1个字节对齐 1
  14. 14   int aa2; //4个字节对齐 01111
  15. 15   short cc2;//2个字节对齐 11
  16. 16   char dd2; //1个字节对齐 1
  17. 17 } testlength2;
  18. 18 int length2 = sizeof(testlength2); //4个字节对齐,占用字节1011  1111 1000,length = 12
  19. 19
  20. 20 typedef struct
  21. 21 {
  22. 22   char bb3; //1个字节对齐 1
  23. 23   char dd3; //1个字节对齐 1
  24. 24   int aa3; //4个字节对齐 001111
  25. 25   short cc23//2个字节对齐 11
  26. 26
  27. 27 } testlength3;
  28. 28 int length3 = sizeof(testlength3); //4个字节对齐,占用字节1100 1111 1100,length = 12
  29. 29
  30. 30 typedef struct
  31. 31 {
  32. 32   char bb4; //1个字节对齐 1
  33. 33   char dd4; //1个字节对齐 1
  34. 34   short cc4;//2个字节对齐 11
  35. 35   int aa4; //4个字节对齐 1111
  36. 36 } testlength4;
  37. 37 int length4 = sizeof(testlength4); //4个字节对齐,占用字节1111 1111,length = 8
  38. 38
  39. 39 int main(void)
  40. 40 {
  41. 41   printf("length1 = %d.\n",length1);
  42. 42   printf("length2 = %d.\n",length2);
  43. 43   printf("length3 = %d.\n",length3);
  44. 44   printf("length4 = %d.\n",length4);
  45. 45   return 0;
  46. 46 }
复制代码
        改变缺省的对界条件(指定对界)
· 使用伪指令#pragma pack (n),编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。
        这时,对齐规则为:
        1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
        2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
        结合1、2推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
        因此,当使用伪指令#pragma pack (2)时,Test结构体的大小为8,内存布局为11 11 11 10。
        需要注意一点,当结构体中包含一个子结构体时,子结构中的成员按照#pragma pack指定的数值和子结构最大数据成员长度中,比较小的那个进行进行对齐。例子如下:
  1. 1 #pragma pack(8)
  2. 2 struct s1
  3. 3 {
  4. 4   short a;
  5. 5   long b;
  6. 6 };
  7. 7
  8. 8 struct s2
  9. 9 {
  10. 10   char c;
  11. 11   s1 d;
  12. 12   long long e;
  13. 13 };
  14. 14 #pragma pack()
复制代码
        sizeof(s2)的结果为24。S1的内存布局为1100 1111,S2的内存布局为1000 1100 1111 0000 1111 1111。
        例子2按照2个字节对齐时:
  1. 1 #include <stdio.h>
  2. 2 #pragma pack(2)
  3. 3 typedef struct
  4. 4 {
  5. 5   int aa1; //2个字节对齐 1111
  6. 6   char bb1;//1个字节对齐 1
  7. 7   short cc1;//2个字节对齐 011
  8. 8   char dd1; //1个字节对齐 1
  9. 9 } testlength1;
  10. 10 int length1 = sizeof(testlength1); //2个字节对齐,占用字节11 11 10 11 10,length = 10
  11. 11
  12. 12 typedef struct
  13. 13 {
  14. 14   char bb2;//1个字节对齐 1
  15. 15   int aa2; //2个字节对齐 01111
  16. 16   short cc2;//2个字节对齐 11
  17. 17   char dd2; //1个字节对齐 1
  18. 18 } testlength2;
  19. 19 int length2 = sizeof(testlength2); //2个字节对齐,占用字节10 11 11 11 10,length = 10
  20. 20
  21. 21 typedef struct
  22. 22 {
  23. 23   char bb3; //1个字节对齐 1
  24. 24   char dd3; //1个字节对齐 1
  25. 25   int aa3; //2个字节对齐 11 11
  26. 26   short cc23//2个字节对齐 11
  27. 27
  28. 28 } testlength3;
  29. 29 int length3 = sizeof(testlength3); //2个字节对齐,占用字节11 11 11 11,length = 8
  30. 30
  31. 31 typedef struct
  32. 32 {
  33. 33   char bb4; //1个字节对齐 1
  34. 34   char dd4; //1个字节对齐 1
  35. 35   short cc4;//2个字节对齐 11
  36. 36   int aa4; //2个字节对齐 11 11
  37. 37 } testlength4;
  38. 38 int length4 = sizeof(testlength4); //2个字节对齐,占用字节11 11 11 11,length = 8
  39. 39 #pragma pack()
  40. 40 int main(void)
  41. 41 {
  42. 42   printf("length1 = %d.\n",length1);
  43. 43   printf("length2 = %d.\n",length2);
  44. 44   printf("length3 = %d.\n",length3);
  45. 45   printf("length4 = %d.\n",length4);
  46. 46   return 0;
  47. 47 }
复制代码
        另外,还有如下的一种方式:
        · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
        · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
       以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。
↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓ 

 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

拉不拉稀肚拉稀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表