Detailed explanation of the byte alignment problem in c ++ memory, and detailed alignment
I. Introduction to what is byte alignment
In modern computers, memory space is divided by byte. Theoretically, it seems that access to any type of variables can start from any address, however, the actual situation is that when accessing a specific type of variable, it is often accessed at a specific memory address, which requires various types of data accordingRulesArrange the space, rather than the sequential emissions one by one, which is alignment.
Reasons and functions of byte alignment
The processing of buckets varies greatly by hardware platform. Some platforms can only access certain types of data from some specific addresses. For example, some architectures may encounter errors when the CPU accesses a variable that is not aligned, so in this architecture, programming must ensure byte alignment. this may not be the case for other platforms, but the most common problem is that alignment of data storage according to the requirements of their platforms may cause a loss of access efficiency. For example, some platforms start from the even address each time they read data. If an int type (assuming a 32-bit System) is stored at the beginning of the even address, then a read cycle can read the 32bit data. If the data is stored at the beginning of the odd address, two read cycles are required, the 32-bit data can be obtained only when the high and low bytes of the two read results are pieced together. Obviously, reading efficiency is greatly reduced.
2. Please refer to the following structure
struct MyStruct { double dda1; char dda; int type };
What will happen if sizeof is used for the structure MyStruct? What is sizeof (MyStruct? You may ask:
Sizeof (MyStruct) = sizeof (double) + sizeof (char) + sizeof (int) = 13
However, when the above structure is tested in VC, you will find that sizeof (MyStruct) is 16. Do you know why such a result is obtained in VC?
In fact, this is a special processing of variable storage by VC. To increase the CPU storage speed, VC performs "alignment" on the starting addresses of some variables. By default, VC specifies that the offset of the starting address of each member variable to the starting address of the structure must be a multiple of the bytes occupied by the variable type.
Common alignment modes (vc6.0, 32-bit System)
Type
Alignment (offset of the starting address of the variable to the starting address of the structure)
Char
The offset must be a multiple of sizeof (char), that is, 1.
Int
The offset must be a multiple of sizeof (int), that is, 4.
Float
The offset must be a multiple of sizeof (float), that is, 4.
Double
The offset must be a multiple of sizeof (double), that is, 8.
Short
The offset must be a multiple of sizeof (short), that is, 2.
When each member variable is stored, the space is requested in sequence based on the order in which the structure appears, and the positions are adjusted according to the alignment above. The vacant byte VC is automatically filled. At the same time, to ensure that the size of the structure is a multiple of the number of byte boundary values of the structure (that is, the number of bytes occupied by the Type occupying the maximum space in the structure, therefore, after applying for space for the last member variable, the vacant bytes will be automatically filled as needed.
The following uses the previous example to illustrate how VC stores the structure.
struct MyStruct { double dda1; char dda; int type };
When allocating space for the above structure, VC displays the order and alignment of the member variables,
(1) Allocate space for the first member dda1. The starting address is the same as the starting address of the structure (the offset 0 is just a multiple of sizeof (double ), this member variable occupies sizeof (double) = 8 bytes;
(2) Allocate space for the second member dda. At this time, the offset of the next address that can be allocated to the starting address of the structure is 8, which is a multiple of sizeof (char, therefore, the dda is stored in an alignment where the offset is 8. The member variable occupies sizeof (char) = 1 byte;
(3) Next, allocate space for the third member type. At this time, the offset of the next address that can be allocated to the starting address of the structure is 9, not a multiple of sizeof (int) = 4, to meet the offset constraints of alignment, VC automatically fills three bytes (these three bytes are not included), and the next allocable (4) the offset of the starting address of the structure is 12, which is just a multiple of sizeof (int) = 4. Therefore, the type is stored in the location where the offset is 12, this member variable occupies sizeof (int) = 4 bytes;
(5) The member variables of the entire structure are allocated with space. The total occupied space is 8 + 1 + 3 + 4 = 16, it is just a multiple of the number of byte boundary values of the structure (that is, the number of bytes occupied by the largest space type in the structure sizeof (double) = 8). Therefore, no vacant bytes need to be filled. Therefore, the size of the entire structure is sizeof (MyStruct) = 8 + 1 + 3 + 4 = 16. Among them, three bytes are automatically filled by VC and nothing makes sense.
Next, let's take another example to change the position of the member variable of MyStruct above to the following:
struct MyStruct { char dda; double dda1; int type };
How much space does this structure occupy? In the VC6.0 environment, we can obtain that sizeof (MyStruc) is 24. Based on the space allocation principles mentioned above, we will analyze how VC allocates space for the above structure. (Simple description)
Struct MyStruct {char dda; // The offset is 0. The alignment is satisfied. dda occupies 1 byte. double dda1; // the offset of the next available address is 1, it is not a multiple of sizeof (double) = 8. You need to add 7 bytes to make the offset change to 8 (matching the alignment). Therefore, VC automatically fills in 7 bytes, dda1 is stored on an address with an offset of 8, which occupies 8 bytes. Int type; // the offset of the next available address is 16, which is a multiple of sizeof (int) = 4. The alignment of int is met, so no VC filling is required, type is stored on the address with the offset of 16, which occupies 4 bytes. }; // All member variables are allocated with space. The total space size is 1 + 7 + 8 + 4 = 20, it is not a multiple of the number of section boundary of the structure (that is, the number of bytes occupied by the Type occupying the maximum space in the structure sizeof (double) = 8), so four bytes need to be filled, the value must be a multiple of sizeof (double) = 8.
Therefore, the total size of this structure is: sizeof (MyStruc) is 1 + 7 + 8 + 4 + 4 = 24. Among them, 7 + 4 = 11 bytes are automatically filled by VC, and nothing makes sense.
The special processing of the structure storage by VC does increase the speed of the CPU storage variable, but sometimes it also brings some trouble. We also shield the default alignment of the variables, you can set the alignment of variables.
# Pragma pack (n) is provided in VC to set the variable to n-byte alignment. N-byte alignment means the offset of the Start address of the variable:
(1) If n is greater than or equal to the number of bytes occupied by the variable, the offset must satisfy the default alignment,
(2) If n is less than the number of bytes occupied by the type of the variable, the offset is a multiple of n, and the default alignment is not required.
The total size of the structure also has a constraint, which is divided into the following two cases: if n is greater than the number of bytes occupied by all member variable types, the total size of the structure must be a multiple of the space occupied by the largest variable; otherwise, it must be a multiple of n (smaller than the two .)
The following is an example of its usage.
# Pragma pack (push) // save alignment # pragma pack (4) // set to 4-byte alignment struct test {char m1; double m4; int m3 ;}; # pragma pack (pop) // restore alignment
The size of the above structure is 16. Next we will analyze its storage situation. First, we will allocate space for m1, and its offset is 0, which meets our own alignment (4-byte alignment ), m1 occupies 1 byte. Then we start to allocate space for m4. At this time, the offset is 1 and three bytes need to be supplemented. In this way, the offset must be a multiple of n = 4 (because sizeof (double) is greater than n ), m4 occupies 8 bytes. Then allocate space for m3. At this time, the offset is 12, which must be a multiple of 4. m3 occupies 4 bytes. At this time, space has been allocated for all member variables. A total of 16 bytes are allocated, which is a multiple of n. If you change # pragma pack (4) to # pragma pack (16), the size of the structure is 24. (Please analyze it by yourself)
3. Let's look at the example below.
#pragma pack(8)struct S1{char a;long b;};struct S2 {char c;struct S1 d;long long e;};#pragma pack()
The sizeof (S2) result is 24.
An important condition for member alignment is that each member is aligned separately. that is, each member is aligned in its own way. that is to say, although alignment by 8 bytes is specified above, not all Members are alignment by 8 bytes. the alignment rule is that each member has a smaller alignment according to its type alignment parameter (usually the size of this type) and the specified alignment parameter (8 bytes here. in addition, the length of the structure must be an integer multiple of all alignment parameters used.
In S1, member a is 1 byte, which is aligned by 1 byte by default, and the specified alignment parameter is 8. Among the two values, 1 and a are aligned by 1 byte. Member B is 4 bytes, the default value is 4-byte alignment. In this case, it is 4-byte alignment, so sizeof (S1) should be 8;
In S2, c is the same as a in S1. It is aligned by 1 byte, while d is a structure. It is 8 bytes. What is its alignment? For the structure, its default alignment is the largest of all its members using alignment parameters, and S1 is 4. therefore, member d is aligned in 4 bytes. the member e is 8 bytes, which is aligned by 8 bytes by default. It is the same as the specified one, so it is connected to the boundary of 8 bytes. At this time, 12 bytes are used, therefore, four bytes are added, and the member e is placed starting from 16th bytes. at this time, the length is 24 and can be divisible by 8 (member e is aligned by 8 bytes. in this way, a total of 24 bytes are used.
A B
Memory layout of S1: 11 **, 1111,
C S1.a S1. B d
Memory layout of S2: 1 ***, 11 ***, 1111, *** 11111111
There are three important points:
(1) Each member is aligned in its own way and can minimize the length.
(2) The default alignment of complex types (such as structures) is the alignment of its longest member, so that the length can be minimized when the member is a complex type.
(3) The alignment length must be an integer multiple of the largest alignment parameter in the member, so that each item can be aligned when processing the array.
Iv. Summary memory Alignment Calculation method:
(1) check whether the offset address of the variable is an integer multiple of the variable size.
(2) check whether the total size after alignment is an integer multiple of the longest variable.