I. Preliminary explanation of memory alignment memory alignment can be summed up in one sentence:
"Data items can only be stored in memory locations where the address is an integer multiple of the size of the data item"For example, the int type occupies 4 bytes, the address can only be in the 0,4,8 position.
Example 1: #include <stdio.h>
struct xx{
Char b;
int A;
int C;
Char D;
};int Main ()
{
struct XX BB;
printf ("&a =%p\n", &BB.A);
printf ("&b =%p\n", &bb.b);
printf ("&c =%p\n", &BB.C);
printf ("&d =%p\n", &BB.D);
printf ("sizeof (XX) =%d\n", sizeof (struct xx)); return 0;
The result of the operation is as follows: &a = Ffbff5ec
&b = Ffbff5e8
&c = ffbff5f0
&d = Ffbff5f4
sizeof (XX) = 16 will find that 3 bytes have been vacated between B and a. That is, after b the 0xffbff5e9,0xffbff5ea,0xffbff5eb is empty, a is stored directly in the 0XFFBFF5EC. Since the size of a is 4. Can only be stored in a position of 4 integer multiples. The size of the printed xx will be found, is 16. Some people may ask, B after 3 bytes, that should also be 13 ah? What about the other 3? This later read this article will understand a bit more in-depth, here is a simple to say is the 3 bytes behind D. will also be wasted. That is, the 3 bytes are also occupied by the structure. Can easily change the structure of the structure. To reduce the use of memory, such as the ability to define a struct as:
struct xx{
Char b;
Char D;
int A;
int C;
The size of this structure is 12. Save a lot of space, can see. When defining the structure of the body. Be sure to consider the effect of memory alignment so that our program takes up less memory.
Two. The default alignment factor for the operating system each operating system has its own default memory alignment factor, assuming that the new version number of the operating system, the default justification factor is generally 8, because the operating system defines the largest type of storage unit is 8 bytes, such as Long long(why must be so.) will be explained in section III). There is no more than 8 bytes of type (such as int is 4,char is 1,long at 32-bit compile time is 4, 64-bit compile time is 8). When the default alignment factor for the operating system conflicts with the theory of memory alignment as described in section I. The operating system's alignment factor is the benchmark.
For example, if the default alignment factor for an operating system is 4, then a variable of type long long does not satisfy the first section, meaning that a long long structure can be stored in a position divisible by 4 and stored in a position divisible by 8. The ability to change the default alignment coefficients of the operating system through the #pragma pack () statement does not suggest changing the default alignment factor when coding, and in section three explains the reason Example 2: #include <stdio.h>
#pragma pack (4)
struct xx{
Char b;
Long Long A;
int C;
Char D;
};
#pragma pack () int main ()
{
struct XX BB;
printf ("&a =%p\n", &BB.A);
printf ("&b =%p\n", &bb.b);
printf ("&c =%p\n", &BB.C);
printf ("&d =%p\n", &BB.D);
printf ("sizeof (XX) =%d\n", sizeof (struct xx)); return 0;
}
Print Result: &a = ffbff5e4
&b = Ffbff5e0
&c = Ffbff5ec
&d = ffbff5f0
sizeof (XX) = 20 finds A that occupies 8 bytes, stored in a location that cannot be divisible by 8. stored in a position divisible by 4. The default alignment factor for the operating system is taken. Three. Why the memory alignment occurs
Memory alignment is a strategy that the operating system takes to get high-speed access to memory, simply by placing two visits to the variable. When visiting memory, the operating system reads a certain length each time (this is the default alignment factor for the operating system, or an integer multiple of the default alignment factor). Assuming there is no memory alignment, in order to read a variable yes, it generates two visits to the bus. For example, if there is no memory alignment. The variable position of the struct XX will appear for example the following situation: struct xx{
Char b; 0xffbff5e8
int A; 0xffbff5e9
int C; 0xffbff5ed
Char D; 0xffbff5f1
The operating system reads the 0XFFBFF5E8-0XFFBFF5EF memory first, then reads the 0XFFBFF5F0-0XFFBFF5F8 's memory, in order to obtain the value C, needs to merge two sets of memory together. This significantly reduces memory access efficiency. (This involves the commonplace problem of space and efficiency which is more important?) No discussion here). This allows you to understand why the first variable of a struct, regardless of its type, is divisible by 8 (since access to memory is from an integer multiple of 8, in order to add read efficiency)!
The problem of memory alignment mainly exists in understanding the distribution of complex structures such as structs in memory. The first step is to identify the concept of memory alignment.
Many real computer systems have limitations on the location of basic types of data stored in memory. They will require that the value of the first address of the data be a multiple of a number k (usually 4 or 8). This is called memory alignment.
This k differs from one compiler to another under different CPU platforms. For example, 32-bit word-length computers and 16-bit word-length computers. This is a bit far from us.
Our development mainly involves two big platforms. Windows and Linux (Unix). The compilers involved are also primarily Microsoft compilers (such as CL), and GCC. The purpose of memory alignment is to make the first address of each basic data type a multiple of the corresponding K, which is the ultimate magic weapon for understanding memory alignment. In addition, the differences between compilers are also distinguished.
This makes it clear that these two points will basically fix all of the memory alignment issues.
K in different compilers:
1. For Microsoft compilers, the size of each base type is this K. The char type is roughly 8. int is 32,long to 32. Double is 64.
2, for the GCC compiler under Linux, the specified size is less than or equal to 2. The k value is its size, which is greater than or equal to 4 of 4. It is clear that the memory distribution of the composite structure, such as struct, should be clearly explained above. Here's a look at the simplest type: Members in a struct are basic data types. Like what:
struct TEST1
{
Char A;
Short B;
int C;
Long D;
Double E;
In the Windows platform, under the Microsoft compiler: if starting from the 0 address, first the K value of a is 1, and its first address is able to make any position. So a occupies the first byte. That is, address 0; then the k value of B is 2, and his first address must be a multiple of 2, not 1, so the address 1 that byte is populated. The first address is address 2, occupies address 2, 3, and then to c,c the K value is 4, his first address is a multiple of 4. So the first address is 4, which takes the address 4, 5. 6. 7. Then the K value to D,d is also 4. So his first address was 8, occupying address 8, 9. 10,11.
Finally to E, his K value is 8. The first address is a multiple of 8, so the address 12,13,14. 15 are populated. His first address should be 16, occupying address 16-23. Obviously its size is 24. This is the distribution of test1 in memory. We create a variable of type test1, A, B, C, D, E are assigned values 2, 4, 8, 16, 32 respectively.
Then, from the low address, print out each byte in memory the corresponding 16 binary number is:
2 0 4 0 8 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 40 40 Verify:
It is clear that the judgment is correct.
On the Linux platform, under the GCC compiler:
If you start from 0 address. First the k value of a is 1, and its first address is able to make any position, so a occupies the first byte. That is address 0. Then the k value of B is 2, and his first address must be a multiple of 2. It can't be 1. So address 1 that byte is populated, the B header address is address 2, occupies address 2. 3; then to C. The k value of C is 4. His first address is a multiple of 4, so the first address is 4. Occupy address 4. 5,6. 7; then the K value to D,d is also 4. So his first address was 8, occupying address 8, 9. 10. 11.
Finally to E, from here to start with the Microsoft compiler differs, his K value is not 8, is still 4, so its first address is 12, occupy address 12-19. Obviously its size is 20. Verify:
We create a variable of type test1. A, B, C, D, E are assigned values 2, 4, 8, 16, 32 respectively.
Then, from the low address, print out each byte in memory the corresponding 16 binary number is:
2 0 4 0 8 0 0 0 10 0 0 0 0 0 0 0 0 0 40 40 It is clear that the judgement is also correct. Next, take a look at a few special cases. To avoid trouble. No longer describe the memory distribution, just calculate the size of the structure. The first type: nested structure
struct TEST2
{
Char F;
struct Test1 g;
}, on the Windows platform. Under the Microsoft compiler: Assuming that the second member of Test2 is taken apart to study the memory distribution, it is possible to know that the member F of the Test2 occupies address 0. G.A occupy address 1, after the memory distribution is not changed, still meet all the basic data members of the first address is its corresponding k multiples of this principle, then test2 size is still 24. But actually the size of the test2 is 32. This is due to the fact that the test1 memory distribution cannot be changed due to the TEST2 structure. So in order for the Test1 species to still meet the alignment requirements, F members need to fill a certain number of bytes behind, it is not difficult to find. This number should be 7, the ability to ensure test1 alignment. So test2 adds 8 bytes relative to Test1, so the size of the test2 is 32.
Under the Linux platform, the GCC compiler: the same, assuming that the second member of the Test2 is taken apart to study the memory distribution, then be able to know that the Test2 member F occupies address 0, G.A occupy address 1, after the memory distribution is not changed, still meet all the basic data members of the first address is its corresponding k multiples of this principle, then test2 size is still 20. But in fact the size of the test2 is 24, the same is because: can not be changed due to the structure of test2 test1 memory distribution, so in order to make the Test1 species are still satisfied with the alignment requirements, F members need to fill a certain number of bytes, it is not difficult to find that the number should be 3. Ability to ensure the alignment of test1.
So test2 adds 4 bytes relative to Test1, so the size of the test2 is 24.
Another: bit-aligned struct TEST3
{
unsigned int a:4;
unsigned int b:4;
char c;
};
Or
struct TEST3
{
unsigned int a:4;
int b:4;
char c;
}; Under the Windows platform, under the Microsoft compiler: multiple contiguous numbers of the same type (signed and unsigned, just the same as the basic type. Also for the same number), assuming that they occupy no more than the base type size. Then they can be viewed as a whole. The different types of numbers follow the respective alignment.
such as: in Test3. A, B can be used as a whole. They are treated as an int data, so the size of the Test3 is 8 bytes. And the values of A and B are arranged in memory starting from the low, and the first 0-3 bits and 4-7 bits in the 4-byte area are assumed to be in the TEST4 bit format
struct TEST4
{
unsigned int a:30;
unsigned int b:4;
char c;
};
Then the size of the Test4 is 12 bytes, and the values of A and B are distributed in the first 30 bits of 4 bytes, respectively. and the second 4 bytes of the first 4 bits.
If over Test5 is the following form
struct TEST5
{
unsigned int a:4;
unsigned char b:4;
char c;
}; then because of the different types of int and char. They are aligned in their own way, so the size of the test5 should be 8 bytes, and the values of A and B are in the first 4 bits and the 5th byte, respectively, at the top 4 bits of a 4 byte. On the Linux platform. gcc compiler: struct TEST3
{
unsigned int a:4;
unsigned int b:4;
char c;
};
GCC, adjacent to each member. Whether the type is the same. When the sum of the digits exceeds the size of the first of these members, the value in the structure is 1 aligned with the K value, and the K value outside the structure is its base type.
Do not exceed in the case of in-memory sequence.
such as Test3. Its size is 4. The value of a, B, in memory, is sequentially ranked in the first four bytes of 0-3 and 4-7 bits.
Suppose test4 bit the following format
struct TEST4
{
unsigned int a:20;
unsigned char b:4;
char c;
};
The size of the Test4 is 4 bytes, and the values of A and B are distributed in the first 4 bytes of 0-19 bits, and 20-23 bits, and c is stored in the 4th byte.
If over Test5 is the following form
struct TEST5
{
unsigned int a:10;
unsigned char b:4;
Short C;
Then the size of the test5 should be 4 bytes, a. The values for B are 0-9-bit and 10-13-bit. c is stored in the second two bytes.
Suppose the size of a becomes 20.
Then the size of the test5 should be 8 bytes.
That is, struct TEST6
{
unsigned int a:20;
unsigned char b:4;
Short C;
}; At this time, Test6 A, B occupies a total of 2 total of 3 bytes, C, the K-value of 0,1,2, can actually be 4-bit first position, but outside the structure, a to be in the way of int alignment. In other words, two consecutive Test6 objects are stored in memory, the first place of a is guaranteed to be a multiple of 4. Then C must be populated with more than 2 bits. So the size of the Test6 is 8 bytes. The part about the structure of the bit segment is more complex. I knew so much on the temporary.
How Windows and Linux memory are aligned