Learn how to correctly use byte alignment and #pragma pack in C language in 1 minute

The default byte alignment of the C/C++ compiler is natural alignment. That is, by default, the compiler allocates space for each variable or data unit according to its natural boundary conditions.

  In a structure, the compiler allocates space for each member of the structure according to its natural alignment. Members are stored sequentially in memory in the order in which they are declared (there may be null bytes inserted between members), and the address of the first member is the same as the address of the entire structure.

  The compiler's default natural bounding condition for structure members is "N-byte alignment", where N is the length of the member data type. For example, the natural alignment condition for int-type members is 4-byte alignment, while the natural alignment condition for double-type structure members is 8-byte alignment. If the starting offset of the member is not located on the "default natural boundary condition" of the member, an appropriate number of null bytes will be added after the previous section.

        The compiler's default natural boundary condition for the entire structure is: the maximum natural boundary condition required among all members of the structure. If the sum of the lengths of each member of the structure is not "an integer multiple of the natural boundary condition of the entire structure", null bytes will be filled after the last member.

        Example 1 (Analyze the default byte boundary conditions for each member of the structure and the default byte boundary conditions for the entire structure):

Copy code
1  struct Test
 2  { 
 3    char x1; // Member x1 is of char type (its starting address must be 1 byte bounded), and its offset address is 0 
4    char x2; // Member x2 is of char type (its starting address The address must be 1 byte bounded, and its offset address is 1 
5    float x3; // Member x3 is of float type (its starting address must be 4 bytes bounded), and the compiler fills two spaces between x2 and x3 Null byte, its offset address is 4 
6    char x4; // Member x4 is char type (its starting address must be 1 byte bounded), its offset address is 8 
7 };
Copy code

2999540-20230505205103748-1768287230.gif

        In the Test structure, the largest member is float x3, so the natural bounding condition of the structure is 4-byte alignment. Then the length of the structure is 12 bytes, and the memory layout is 1100 1111 1000.

Copy code
1 #include <stdio.h>
 2 typedef struct 
3  {
 4    int aa1; // 4 bytes aligned 1111 
5    char bb1; // 1 byte aligned 1 
6    short cc1; // 2 bytes aligned 011 
7    char dd1; // 1 byte aligned 1 
8  } testlength1;
 9  int length1 = sizeof (testlength1); // 4 bytes aligned, occupying bytes 1111 1011 1000, length = 12 
10  
11 typedef struct 
12  {
 13    char bb2;// 1 byte aligned 1 
14    int aa2; // 4 bytes aligned 01111 
15    short cc2; // 2 bytes aligned 11 
16    char dd2; // 1 byte aligned 1 
17  } testlength2;
 18  int length2 = sizeof (testlength2); // 4 bytes aligned, occupying bytes 1011 1111 1000, length = 12 
19  
20 typedef struct 
21  {
 22    char bb3; // 1 byte aligned 1 
23    char dd3; // 1 Bytes aligned 1 
24    int aa3; //4 bytes aligned 001111 
25    short cc23 // 2 bytes aligned 11 
26  
27  } testlength3;
 28  int length3 = sizeof (testlength3); // 4 bytes aligned, occupying bytes 1100 1111 1100, length = 12 
29  
30 typedef struct 
31  {
 32    char bb4; // 1 byte aligned 1 
33    char dd4; // 1 byte aligned 1 
34    short cc4; // 2 bytes aligned 11 
35    int aa4; // 4 words Section alignment 1111 
36  } testlength4;
 37 int length4 = sizeof (testlength4); // 4 bytes aligned, occupying bytes 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 }
Copy code

        改变缺省的对界条件(指定对界)
· 使用伪指令#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指定的数值和子结构最大数据成员长度中,比较小的那个进行进行对齐。例子如下:

Copy code
 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()
Copy code

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

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

Copy code
 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 }
Copy code
2999540-20230505205103748-1768287230.gif

        In addition, there is another way:

        · __attribute((aligned (n))), so that the structure members it acts on are aligned on the natural boundary of n bytes. If the length of any member in the structure is greater than n, it is aligned according to the length of the largest member.

        · __attribute__ ((packed)), cancels the optimized alignment of the structure during compilation, and aligns it according to the actual number of bytes occupied.

       The above n = 1, 2, 4, 8, 16... The first method is more common.

Guess you like

Origin blog.csdn.net/Gefangenes/article/details/132483935