10

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

 1 year ago
source link: https://www.cnblogs.com/Sharemaker/p/17375352.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

​  C/C++编译器的缺省字节对齐方式为自然对界。即在缺省情况下,编译器为每一个变量或是数据单元按其自然对界条件分配空间。

  在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储(成员之间可能有插入的空字节),第一个成员的地址和整个结构的地址相同。

  编译器缺省的结构成员自然对界条件为“N字节对齐”,N即该成员数据类型的长度。如int型成员的自然对界条件为4字节对齐,而double类型的结构成员的自然对界条件为8字节对齐。若该成员的起始偏移不位于该成员的“默认自然对界条件”上,则在前一个节面后面添加适当个数的空字节。

        编译器缺省的结构整体的自然对界条件为:该结构所有成员中要求的最大自然对界条件。若结构体各成员长度之和不为“结构整体自然对界条件的整数倍”,则在最后一个成员后填充空字节。

        例子1(分析结构各成员的默认字节对界条界条件和结构整体的默认字节对界条件):

1 struct Test
2 { 
3   char x1; // 成员x1为char型(其起始地址必须1字节对界),其偏移地址为0 
4   char x2; // 成员x2为char型(其起始地址必须1字节对界,其偏移地址为1 
5   float x3; // 成员x3为float型(其起始地址必须4字节对界),编译器在x2和x3之间填充了两个空字节,其偏移地址为4 
6   char x4; // 成员x4为char型(其起始地址必须1字节对界),其偏移地址为8 
7 };

2999540-20230505205103748-1768287230.gif

        在Test结构体中,最大的成员为float x3,因此结构体的自然对界条件为4字节对齐。则结构体长度就为12字节,内存布局为1100 1111 1000。

 1 #include <stdio.h>
 2 typedef struct
 3 {
 4   int aa1; //4个字节对齐 1111
 5   char bb1;//1个字节对齐 1
 6   short cc1;//2个字节对齐 011
 7   char dd1; //1个字节对齐 1
 8 } testlength1;
 9 int length1 = sizeof(testlength1); //4个字节对齐,占用字节1111 1011 1000,length = 12
10 
11 typedef struct
12 {
13   char bb2;//1个字节对齐 1
14   int aa2; //4个字节对齐 01111
15   short cc2;//2个字节对齐 11
16   char dd2; //1个字节对齐 1
17 } testlength2;
18 int length2 = sizeof(testlength2); //4个字节对齐,占用字节1011  1111 1000,length = 12
19 
20 typedef struct
21 {
22   char bb3; //1个字节对齐 1
23   char dd3; //1个字节对齐 1
24   int aa3; //4个字节对齐 001111
25   short cc23//2个字节对齐 11
26 
27 } testlength3;
28 int length3 = sizeof(testlength3); //4个字节对齐,占用字节1100 1111 1100,length = 12
29 
30 typedef struct
31 {
32   char bb4; //1个字节对齐 1
33   char dd4; //1个字节对齐 1
34   short cc4;//2个字节对齐 11
35   int aa4; //4个字节对齐 1111
36 } testlength4;
37 int length4 = sizeof(testlength4); //4个字节对齐,占用字节1111 1111,length = 8
38 
39 int main(void)
40 {
41   printf("length1 = %d.\n",length1);
42   printf("length2 = %d.\n",length2);
43   printf("length3 = %d.\n",length3);
44   printf("length4 = %d.\n",length4);
45   return 0;
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 #pragma pack(8)
 2 struct s1
 3 {
 4   short a;
 5   long b;
 6 };
 7 
 8 struct s2
 9 {
10   char c;
11   s1 d;
12   long long e;
13 };
14 #pragma pack()

        sizeof(s2)的结果为24。S1的内存布局为1100 1111,S2的内存布局为1000 1100 1111 0000 1111 1111。

        例子2按照2个字节对齐时:

 1 #include <stdio.h>
 2 #pragma pack(2)
 3 typedef struct
 4 {
 5   int aa1; //2个字节对齐 1111
 6   char bb1;//1个字节对齐 1
 7   short cc1;//2个字节对齐 011
 8   char dd1; //1个字节对齐 1
 9 } testlength1;
10 int length1 = sizeof(testlength1); //2个字节对齐,占用字节11 11 10 11 10,length = 10
11 
12 typedef struct
13 {
14   char bb2;//1个字节对齐 1
15   int aa2; //2个字节对齐 01111
16   short cc2;//2个字节对齐 11
17   char dd2; //1个字节对齐 1
18 } testlength2;
19 int length2 = sizeof(testlength2); //2个字节对齐,占用字节10 11 11 11 10,length = 10
20 
21 typedef struct
22 {
23   char bb3; //1个字节对齐 1
24   char dd3; //1个字节对齐 1
25   int aa3; //2个字节对齐 11 11
26   short cc23//2个字节对齐 11
27 
28 } testlength3;
29 int length3 = sizeof(testlength3); //2个字节对齐,占用字节11 11 11 11,length = 8
30 
31 typedef struct
32 {
33   char bb4; //1个字节对齐 1
34   char dd4; //1个字节对齐 1
35   short cc4;//2个字节对齐 11
36   int aa4; //2个字节对齐 11 11
37 } testlength4;
38 int length4 = sizeof(testlength4); //2个字节对齐,占用字节11 11 11 11,length = 8
39 #pragma pack()
40 int main(void)
41 {
42   printf("length1 = %d.\n",length1);
43   printf("length2 = %d.\n",length2);
44   printf("length3 = %d.\n",length3);
45   printf("length4 = %d.\n",length4);
46   return 0;
47 }
2999540-20230505205103748-1768287230.gif

        另外,还有如下的一种方式:

        · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。

        · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

       以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。


↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓ 

2999540-20230505204944803-1755904833.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK