Examples illustrate structural alignment in C programming

  • 2020-05-10 18:30:23
  • OfStack

Q: what are the principles of structural alignment?
A: instead of talking about how many bytes are aligned with a structure, let's look at the case where it is aligned with only 1 byte:


#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)   printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member) ((char *)&((struct *)0)->member - (char *)0)

#pragma pack(1)

typedef struct
{
  char  sex;
  short  score;
  int   age;
}student;

int main()
{
  PRINT_D(sizeof(student))
  PRINT_D(OFFSET(student,sex))
  PRINT_D(OFFSET(student,score))
  PRINT_D(OFFSET(student,age))
  return 0;
}

Output:


sizeof(student) is 7
OFFSET(student,sex) is 0
OFFSET(student,score) is 1
OFFSET(student,age) is 3

As you can see, if aligned by 1 byte, the members within the structure are closely aligned, sizeof(char) == 1, sizeof(short) == 2, sizeof(int) == 4.

Modify the above code to remove the #pragma pack statement. The code is as follows:


#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)   printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct,member) ((char *)&((struct *)0)->member - (char *)0)

typedef struct
{
  char  sex;
  short  score;
  int   age;
}student;

int main()
{
  PRINT_D(sizeof(student))
  PRINT_D(OFFSET(student,sex))
  PRINT_D(OFFSET(student,score))
  PRINT_D(OFFSET(student,age))
  return 0;
}

Operation results:


sizeof(student) is 8
OFFSET(student,sex) is 0
OFFSET(student,score) is 2
OFFSET(student,age) is 4

At this point, the members are not as close as before, but there are some gaps. Here are the alignment principles:

This principle applies when the #pragma pack statement is not in effect (it may vary from platform to platform) :

Principle A: a member of struct or union. The first member is offset by 0, and the starting position of each member after that must be an integer multiple of the current member size.

Principle B: if the structure A contains structure members B, then the starting position of B must be an integer multiple of the maximum element size in B.

Principle C: the total size of a structure must be an integer multiple of its largest internal member;

According to the above three principles, let's make a specific analysis: sex is at offset 0, accounting for 1 byte; score is of type short, which takes 2 bytes. score must start at an integer multiple of 2, so its starting position is 2. age is of type int and has a size of 4 bytes. It must start at an integer multiple of 4, since sex takes 1 byte in front, 1 byte filled and score takes 2 bytes in front. Address 4 is already an integer multiple of 4, so the location of age is 4.

Go ahead and modify the code above and add the #pragma pack statement:


#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)   printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct, member) ((char *)&((struct *)0)->member - (char *)0)

#pragma pack(4)

typedef struct
{
  char  sex;
  short  score;
  int   age;
}student;

int main()
{
  PRINT_D(sizeof(student))
  PRINT_D(OFFSET(student,sex))
  PRINT_D(OFFSET(student,score))
  PRINT_D(OFFSET(student,age))
  return 0;
}

Operation results:


sizeof(student) is 8
OFFSET(student,sex) is 0
OFFSET(student,score) is 2
OFFSET(student,age) is 4

Specific analysis:

With the #pragma pack(4) statement, the principles A and C do not apply. The actual alignment principle is the smaller of the self-aligning value (member sizeof size) and the specified alignment value (#pragma pack specified alignment size). In order principle, sex is still offset to 0, its alignment value is 1, and the alignment value is specified to be 4, so the actual alignment is 1. The alignment value of score members is 2, and the alignment value is 4, but the actual alignment value is 2. So the front sex is going to be filled with 1 byte, and then the position of score, which is offset by 2; age has its own alignment value of 4, which is specified as 4, so the actual alignment value is 4. The preceding sex and score take exactly 4 bytes, so age is stored next; It has an offset of 4.

Q: on the question of bit fields, what does the airspace represent?
A: it represents the subsequent bit field starting from the new space.


#include <stdio.h>
#include <string.h>

#define PRINT_D(intValue)   printf(#intValue" is %d\n", (intValue));
#define OFFSET(struct, member) ((char *)&((struct *)0)->member - (char *)0)

typedef struct 
{
  int a : 1;
  int b : 3;
  int : 0;
  int d : 2;
}bit_info;

int main()
{
  PRINT_D(sizeof(bit_info))
  return 0;
}

Operation results:


sizeof(bit_info) is 8

a in bit_info, b takes the first 4 bits of 4 bytes to int:0; , means that all remaining unfilled bits will be filled at this time, that is, the remaining 28 bits of the just 4 bytes; int d: 2; It will start at the fourth byte and take up another four bytes, so the total size is 8.

Let's look at a few more examples
Case 1:


struct A{ 
        char f1 : 3; 
        char f2 : 4; 
        char f3 : 5; 
    };

                                      a           b                   c
Memory layout of A: 111,1111 *,11111 * * *
The bit field type is char, and the first byte can only hold f1 and f2, so f2 is compressed into the first byte, while f3 can only start from the next byte. So sizeof(A) is equal to 2.
Example 2:


struct B{ 
        char f1 : 3; 
        short f2 : 4; 
        char f3 : 5; 
    };

Due to the different types of adjacent bit fields, sizeof is 6 in VC6 and 2 in Dev-C ++.
Example 3:


sizeof(student) is 7
OFFSET(student,sex) is 0
OFFSET(student,score) is 1
OFFSET(student,age) is 3
0

Non-bit field fields are interspersed among them without compression, and the sizes obtained in VC6 and Dev-C ++ are both 3.
Consider a question: why design memory alignment? If the architecture is not aligned, members will be stored next to each other, which is a waste of space. So why use alignment? Alignment and non-alignment of the architecture is a tradeoff in time and space. Alignment saves time. Assuming that an architecture has a word length of w, it also assumes that data of w width is processed most frequently and importantly in this architecture. It is also designed to prioritize the efficiency of operations on w bit data. If you are interested, you can google1, and they can explain a lot of things to you.
Finally, by the way, when designing the structure, 1 will respect 1 habit, which is to put the type with small space in the front and the type with large space in the back, so as to save 1 bit of space.


Related articles: