1: Memory alignment definition:
The memory space in the current computer is divided by byte. Theoretically, it seems that access to any type of variables can start from any address, however, in fact, computer systems have limits on the storage location of basic data types in the memory, requiring that the first address of the data storage be a multiple of a certain number of K, in this way, the various basic data types are arranged according to certain rules in the memory, rather than one next to the other, which is the memory alignment.
Alignment modulus:
The value K specified in the memory alignment is alignment modulus ). When the ratio of the alignment modulus of one type of S to the alignment modulus of another type of T is an integer greater than 1, we call it the alignment requirement of type s stronger than that of T (strict ), t is weaker (loose) than S ).
2: benefits of memory alignment:
Memory alignment is a mandatory requirement. First, it simplifies the design of the transmission system between the processor and the memory, and second, it can speed up Data Reading. 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, the 32bit can be read in a read cycle, if the data is stored at the beginning of the odd address, two read cycles are required, and the high and low bytes of the two read results are pieced together to obtain the 32bit data. Obviously, reading efficiency is greatly reduced.
Intel's ia32 architecture processor works correctly regardless of whether the data is aligned. However, if you want to improve performance, pay attention to the memory alignment mode.
The ansi c standard does not specify that variables declared adjacent must be adjacent in memory. For program efficiency, memory alignment problems are flexibly handled by the compiler, which may cause some padding bytes between adjacent variables. For basic data types (INT char, etc.), the memory space they occupy has a definite value in a specific hardware system. Ansi c specifies that the size of a structure type is the sum of the size of all its fields and the size of the padding areas between or at the end of the field.
3: Memory alignment policy:
Alignment policy of Microsoft C compiler (cl.exe for 80 × 86:
First, the first address of the struct variable can be divisible by the size of its widest basic type member;
Note: When the compiler opens space for the struct, it first finds the widest basic data type in the struct, and then finds the location where the memory address can be divisible by the basic data type, the first address of the struct. Use the size of the widest basic data type as the alignment modulus described above.
Second, the offset of each member of the struct to the first address of the struct is an integer multiple of the member size. If necessary, the compiler will add the internal adding between the members );
Note: before opening a space for a member of the struct, the compiler first checks whether the offset of the first address of the pre-opening space to the first address of the struct is an integer multiple of the current member. If yes, It stores the member, on the contrary, a certain number of bytes are filled between the current member and the previous Member to meet the integer double requirement, that is, the first address of the pre-opened space is removed several bytes.
Third: the total size of the struct is an integer multiple of the size of the widest basic type of the struct. If necessary, the compiler will add the trailing padding after the last member ).
Note: The total size of the struct includes the padding byte. The last member must meet the preceding two conditions and the third condition. Otherwise, the last few bytes must be filled to meet the requirements.
The padding byte is the space allocated to the struct to make the struct field meet the memory alignment requirements. The structure itself also has alignment requirements. The ansi c standard specifies that the alignment requirements of the structure type cannot be looser than the strictest requirements of all its fields, but it can be more strict (but this is not mandatory, vc7.1 just makes them as strict ). The C standard ensures that the space occupied by arrays of any type (including custom structure types) must be equal to the size of a single data of this type multiplied by the number of array elements. In other words, there is no gap between the elements of the array.
The summary rules are as follows:
0: the first address of the struct variable can be divisible by the size of its widest basic type member.
1: The default memory alignment of vc6 and vc71 is # pragam pack (8)
2: each member in the struct is aligned according to its type (usually the size of this type) and a smaller alignment in the specified alignment parameter.
3: The offset of each member of the struct to the first address of the struct is an integer multiple of the member size.
4: The structure itself also has an alignment requirement rule, which cannot be looser than the strictest requirement of all its fields.
5: the total size of the struct is an integer multiple of the size of the widest basic type of the struct, and the memory should be saved as much as possible.
6: in GCC, the maximum alignment modulus is 4. That is to say, the alignment modulus can only be 1 or 2 even if there is a double type in the structure, 4.
In addition, in the above rules, the offset value must be an integer multiple of the member size in the first 3rd:
(1): If the member size is smaller than or equal to 4, it is feasible according to the above rules,
(2): If the size of a member is greater than 4, the offset of each member of the struct to the first address of the struct can only be determined by an integer multiple of 4.
Example 1 (vc8 ):
Typedef struct ms1 {
Char;
Int B;
} Ms1;
Typedef struct MS2 {
Int;
Char B;
} MS2;
The strongest alignment requirement in ms1 is the B field (INT type). The first address offset of field a is 0 (a multiple of 1), which is directly stored. If field B is directly stored, the offset of field B relative to the first address of the struct variable is 1 (not a multiple of 4), which is filled with 3 bytes, and B is stored starting from the offset address of 4. That is to say, 2nd and 3rd rules are followed. For the struct variable itself, the alignment parameter should be at least 4 according to Rule 4. According to Rule 5, sizeof (ms1) = 8; the same is true for the result obtained by MS2 analysis.
Example 2vc8:
Typedef struct ms3 {
Char;
Short B;
Double C;
} Ms3;
Typedef struct MS4 {
Char;
Ms3 B;
} MS4;
In ms3, the most strict memory field is C (8 bytes), and the alignment parameter of ms3 is also 8 bytes; the alignment modulus of the MS4 data type is the same as that of the double type (8) in ms3. Field A should be followed by 7 bytes. sizeof (ms3) = 16; sizeof (MS4) = 24;
Note that in rule 5, the total size of the struct is an integer multiple of the size of the widest basic type of the struct. Note that it is the basic type. Here, ms3 is not the basic type.
The choice of alignment modulus can only be based on the basic data type. Therefore, for the nested struct In the struct, you can only consider the basic data type to be split.
Example 3 (GCC ):
Struct t {
Char ch;
Double D;
};
In GCC, sizeof (t) should be 12 bytes. 16 bytes in vc8.
Ch is 1 byte. No problem. The size of the subsequent D is greater than 4. The alignment modulus of D can only be 4, and the first address offset relative to the struct variable can only be 4, instead, it cannot be an integer multiple of 8, which is stored at the offset of 4. The struct occupies 12 bytes in total.
No 5th rules are executed here.
Bit domain status:
C99 specifies that int, unsigned int, and bool can be bit domain types. However, almost all compilers have extended this to allow the existence of other types.
If the struct contains a bit-field, the summary rules are as follows:
1) if the types of adjacent fields are the same, and the sum of the bit widths is smaller than the sizeof size of the type, the subsequent fields will be stored next to the previous field until they cannot be accommodated;
2) If the Field Types of adjacent bit fields are the same, but the sum of Bit Width is greater than the sizeof size of the type, the subsequent fields start from the new storage unit, its offset is an integer multiple of its type;
3) if the types of adjacent bitfield fields are different, the specific implementation of each compiler varies, vc6 adopts the non-compression mode (the fields of different bit domains are stored in different bit domain type bytes), and both Dev-C ++ and GCC adopt the compression mode;
4) do not compress fields that are interspersed with non-bit fields;
5) the total size of the struct is an integer multiple of the size of the widest basic type of the struct, and the memory should be saved as much as possible.
Note: When the two fields are of different types, for example:
Struct n {
Char C: 2;
Int I: 4;
};
The memory alignment criteria for the non-bit domain struct are still met. the offset of the I member to the first address of the struct should be an integer multiple of 4. Therefore, the C member must be filled with three bytes, then the space of four bytes is opened up as the int type, four of which are used to store I, so the space occupied by the above struct in VC is 8 bytes;
For compilers that adopt compression, the memory alignment criteria of the non-bit domain structure are followed. The difference is that if the three words are filled with energy saving, the data is compressed to the padding byte, which cannot be accommodated. Therefore, the space occupied by the above struct N in GCC or Dev-C ++ should be 4 bytes.
Example 4:
Typedef struct {
Char C: 2;
Double I;
Int C2: 4;
} N3;
According to rule 4, the space occupied by GCC is 16 bytes, and the space occupied by VC is 24 bytes.
Conclusion:
--------
When defining a struct, it is best for Members to define it from large to small, which can save space relatively. For example:
Struct {
Double D;
Int I;
Char C;
};
Therefore, both VC series compilers in windows and GCC in Linux are 16 bytes.
Example 5:
Typedef union student {
Char name [10];
Long SnO;
Char sex;
Float score [4];
} Stu;
Stu AA [5];
Cout <sizeof (AA) <Endl;
Union is variable. The maximum Member of the Union is 16*5 = 5 = 80.
Example 6:
Typedef struct student {
Char name [10];
Long SnO;
Char sex;
Float score [4];
} Stu;
Stu AA [5];
Cout <sizeof (AA) <Endl;
Space occupied by Stu: 10 bytes (char) + null 2 bytes + 4 bytes (long) + 1 byte (char) + null 3 bytes + 16 bytes (float) = 36 bytes, 36*5 = 180 bytes
Example 7 (vc8.0 ):
Typedef struct bitstruct {
Int B1: 5;
Int B2: 2;
Int B3: 3;
} Bitstruct;
Int _ tmain (INT argc, _ tchar * argv []) {
Bitstruct B;
Memcpy (& B, "em", sizeof (B ));
Cout <sizeof (B) <Endl;
Cout <B. B1 <Endl <B. b2 <Endl <B. B3;
Return 0;
}
Bitstruct is a struct containing a bit field. The sizeof (INT) is 4 bytes. According to rules 1 and 2, B1 occupies the first 5 bytes. According to rule 1, b2. B2. B3. B3.
According to Rule 5, obtain sizeof (bitstruct) = 4.
Currently, the mainstream CPU, Intel series uses the little endian format to store data, and Motorola series CPUs use big endian.
Using mainstream little endian analysis:
During memory allocation, the first member type int (4 bytes) of bitstruct is allocated first. The storage of these four bytes follows the principle that low bytes are stored in low addresses.
Int contains four bytes:
4th bytes-3rd bytes-2nd bytes-1st bytes-bytes,
The storage method in the memory is as follows.
Then allocate 5 bits to B1. Here, the priority should be 5 lower bits, that is, the 5 lower bits of the first byte.
Then the two bytes of b2 are allocated, that is, the second byte following the 1st bytes.
Finally, the three bits of B3 are allocated. According to rules 1, 2, and B3, the bits are stored immediately. The bits of B3 are the highest bits of the first byte, and the two bits are the lowest bits of the 2nd byte.
The memory distribution chart is as follows:
The binary character e is 0100 0101, And the binary character m is 0100 1101. It is stored in the memory as follows:
Memcpy is a bitwise copy, so the two memory zones can be directly mapped
The binary format of B1 is: 00101, the high value is 0, positive number, 5
The binary format of B2 is: 10, the high position is 1, negative number, and 1 is reversed, adding the symbol,-2
The binary form of B3 is: the lowest Bit of B3 is 0, the high position is 01, and the splicing result is 010, positive number, 2
The memory allocation situation is quite strange. For example 7, B1 should be 5, B2 is-2, B3 is-6, and vc8.0 is correct.
Typedef struct bitstruct {
Int B1: 5;
Int B2: 2;
Int B3: 4;
} Bitstruct;
Int _ tmain (INT argc, _ tchar * argv []) {
Bitstruct B;
Memcpy (& B, "em", sizeof (B ));
Cout <sizeof (B) <Endl;
Cout <B. B1 <Endl <B. b2 <Endl <B. B3;
Return 0;
}
4: defines the memory layout and memory byte alignment for Arrays
Int B = 10;
Int A [3] = {1, 2, 3 };
Int c = 11;
Int B = 0x01020304;
Char CH = 'a ';
For a number 0x01020304; for a number 0x1122
When using the little endian method, the low address stores the low byte, from the low address to the high address: 4-> 3-> 2-> 1
In the big endian mode, the low address stores the high byte, from the low address to the high address: 1-> 2-> 3-> 4
In the little endian mode, the address of B refers:Low address (stores the lowest byte)
1: void __cdecl func_cdcel(int i, char *szTest) {
2: cout <"sztest address in the stack:" <& sztest <Endl;
3: cout <"sztest value (pointing address):" <(void *) sztest <Endl;
4:
5: cout <"I address in the stack:" <& I <Endl;
6: cout <"I address:" <& I <Endl;
7:
8: int k,k2;
9: cout <"Address of the local variable k:" <& K <Endl;
10: cout <"Address of local variable K2:" <& K2 <Endl;
11: cout << "-------------------------------------------------------" << endl;
12: }
13:
14: void __stdcall func_stdcall(int i, char *szTest){
15: cout <"sztest address in the stack:" <& sztest <Endl;
16: cout <"sztest itself value (pointing address):" <(void *) sztest <Endl;
17:
18: cout <"I address in the stack:" <& I <Endl;
19: cout <"I address:" <& I <Endl;
20:
21: int k,k2;
22: cout <"Address of the local variable k:" <& K <Endl;
23: cout <"Address of local variable K2:" <& K2 <Endl;
24: cout << "-------------------------------------------------------" << endl;
25: }
26:
27: int main(){
28: int a[4];
29: cout <"A [0] address:" <& A [0] <Endl;
30: cout <"A [1] address:" <& A [1] <Endl;
31: cout <"A [2] address:" <& A [2] <Endl;
32: cout <"A [3] address:" <& A [3] <Endl;
33:
34: int i = 0x22;
35: int j = 8;
36: char szTest[4] = {'a','b', 'c', 'd'};
37: cout <"I address:" <& I <Endl;
38: cout <"sztest address:" <(void *) sztest <Endl;
39: func_cdcel(i, szTest);
40: func_stdcall(i, szTest);
41: }
Output:
A [0] address: 0012ff54
A [1] address: 0012ff58
A [2] address: 0012ff5c
A [3] address: 0012ff60 <-the visible storage method is shown in. A [3] is in the high address. First, go to the stack, array address a is a [0] address (low address)
I address: 0012ff48 <-memory alignment is performed here. The starting address of I must be a multiple of the memory size occupied by I.
Sztest address: 0012ff30
Sztest stack address: 0012fe5c
Sztest value (pointing address): 0012ff30
I in the stack address: 0012fe58 <-I in the stack address is lower than sztest, that is, sztest is first in the stack
Address of I: 0012fe58
Address of the local variable k: 0012fe48
Address of local variable K2: 0012fe3c
-------------------------------------------------------
Sztest stack address: 0012fe5c
Sztest value (pointing address): 0012ff30
I in the stack address: 0012fe58
Address of I: 0012fe58
Address of the local variable k: 0012fe48
Address of local variable K2: 0012fe3c
-------------------------------------------------------