Why there is memory alignment
The following excerpt from Intel Architecture Manual.
Word, double word, and four words do not need to be aligned in memory on natural boundaries. (for words, double words, and four words, the natural boundary is an even address, an address that can be divisible by 4, and an address divisible by 8.) )
In any case, in order to improve the performance of the program, data structures (especially stacks) should be aligned as far as possible on natural boundaries. The reason is that in order to access unaligned memory, the processor needs to make two memory accesses; However, aligned memory access requires only one access.
A word or double-word operand spans a 4-byte boundary, or a four-word operand spans 8-byte boundaries, is considered unaligned, and requires two bus cycles to access memory. A word start address is odd but does not span a word boundary is considered to be aligned and can be accessed during a bus cycle.
Some operations with a four-word instruction require that the number of memory operands be aligned on the natural boundary. If the operands are not aligned, these instructions will produce a generic protection exception (#GP). A four-word natural boundary is an address that can be divisible by 16. Other operations with a double four-word instruction allow unaligned access (no generic protection exception), however, additional memory bus cycles are required to access misaligned data in memory.
How to avoid the effect of memory alignment
So, can not only achieve the goal of improving performance, but also to save a bit of space. There is a little bit of skill to use. For example, we can change the above structure to:
struct BAR
{
Char C1;
Char C2;
Short S;
int i;
};
This way, each member is aligned on its natural boundary, thus avoiding the compiler's automatic alignment. In this example, sizeof (bar) = = 8.
This technique has an important role to play, especially when this structure is provided as part of the API for third-party development use. Third-party developers may change the compiler's default alignment options, causing the structure to use some sort of alignment in your published DLL, while the third-party developer uses a different alignment. This will lead to major problems.
For example, the Foo structure, our DLLs use the default alignment option, for arranges
C1 00000000, S 00000002, C2 00000004, I 00000008, while sizeof (foo) = = 12.
And the third party turns the alignment option off, causing
C1 00000000, S 00000001, C2 00000003, I 00000004, while sizeof (foo) = = 8.
How to use the alignment options in C + +
The compilation options in VC6 are/zp[1|2|4|8| /ZP1 represents a 1-byte boundary alignment, and the corresponding/ZPN represents an n-byte boundary alignment. N-byte boundary alignment means that the address of a member must be arranged on an integer-multiple address of the member's size or an integer-multiple address of N, taking the minimum value from them. That is
Min (sizeof (member), N)
In fact, a 1-byte boundary alignment also indicates that there are no voids between struct members.
The/ZPN option is applied to the entire project, affecting all the constructs that participate in the compilation.
To use this option, you can open the Project Properties page in VC6, C + + pages, select the code Generation category, and select the struct member alignment.
To use the alignment options specifically for certain structure definitions, you can use the #pragma pack compilation directives. The instruction syntax is as follows:
#pragma pack ([show] | [Push | pop] [, identifier], N)
The meaning and/ZPN options are the same. Like what:
#pragma pack (1)
struct Foo_pack
{
Char C1;
Short S;
Char C2;
int i;
};
#pragma pack ()
Stack memory Alignment
We can observe that the alignment of the stack in VC6 is not affected by the structure member alignment options. (It's a different thing). It is always aligned and aligned on a 4-byte boundary.
Validation code
#include <stdio.h>
struct FOO
{
Char C1;
Short S;
Char C2;
int i;
};
struct BAR
{
Char C1;
Char C2;
Short S;
int i;
};
#pragma pack (1)
struct Foo_pack
{
Char C1;
Short S;
Char C2;
int i;
};
#pragma pack ()
int main (int argc, char* argv[])
{
Char C1;
Short S;
Char C2;
int i;
struct Foo A;
struct Bar B;
struct Foo_pack p;
printf ("Stack C1%p, S%p, C2%p, I%p/n",
(unsigned int) (void*) &c1-(unsigned int) (void*) &i,
(unsigned int) (void*) &s-(unsigned int) (void*) &i,
(unsigned int) (void*) &C2-(unsigned int) (void*) &i,
(unsigned int) (void*) &i-(unsigned int) (void*) &i);
printf ("struct Foo C1%p, S%p, C2%p, I%p/n",
(unsigned int) (void*) &a.c1-(unsigned int) (void*) &a,
(unsigned int) (void*) &A.S-(unsigned int) (void*) &a,
(unsigned int) (void*) &a.c2-(unsigned int) (void*) &a,
(unsigned int) (void*) &a.i-(unsigned int) (void*) &a);
printf ("struct bar C1%p, C2%p, S%p, I%p/n",
(unsigned int) (void*) &b.c1-(unsigned int) (void*) &b,
(unsigned int) (void*) &b.c2-(unsigned int) (void*) &b,
(unsigned int) (void*) &B.S-(unsigned int) (void*) &b,
(unsigned int) (void*) &B.I-(unsigned int) (void*) &b);
printf ("struct Foo_pack C1%p, S%p, C2%p, I%p/n",
(unsigned int) (void*) &p.c1-(unsigned int) (void*) &p,
(unsigned int) (void*) &p.s-(unsigned int) (void*) &p,
(unsigned int) (void*) &p.c2-(unsigned int) (void*) &p,
(unsigned int) (void*) &p.i-(unsigned int) (void*) &p);
printf ("sizeof foo is%d/n", sizeof (foo));
printf ("sizeof bar is%d/n", sizeof (bar));
printf ("sizeof Foo_pack is%d/n", sizeof (Foo_pack));
return 0;
}
-----------------------------------------------------------------------------------------------------------in the structure , the compiler allocates space for each member of the structure according to its natural boundary conditions, and the individual members are stored sequentially in memory in the order in which they are declared, and the address of the first member is the same as the address of the entire structure. By default, the C compiler allocates space for each variable or data unit according to its natural boundary conditions
For example, the following structure each member space allocation situation
struct Test {
Char X1;
Short X2;
Float X3;
Char x4;
};
The first member of the X1 structure, whose offset address is 0, occupies the 1th byte. The second member, X2, is of type short, and its starting address must be 2-byte bound, so the compiler fills an empty byte between X2 and X1. The third member of the struct, X3, and the fourth member X4, fall exactly on their natural-bound addresses, and do not require additional padding bytes in front of them. In the test structure, the member X3 requires a 4-byte pair of bounds, which is the largest bounded element required by all members of the struct, and thus the natural bounds of the test structure is 4 bytes, and the compiler fills 3 empty bytes after the member X4. The entire structure occupies 12 bytes of space.
Now you know what's going on.
Change the default assignment policy for the C compiler
In general, the default boundary conditions can be changed in two ways:
· Using pseudo-Directives #pragma pack ([n])
· Using command-line arguments at compile time
The #pragma pack ([n]) pseudo-directive allows you to select the alignment policy that the compiler takes to allocate space for the data:
For example, after using the #pragma pack (1) pseudo-directive, the spatial distribution of the members of the test structure is aligned by one byte
#pragma pack (push)//Save alignment status
#pragma pack (1)
#pragma pack (POP)
A What is byte alignment and why is it aligned
The memory space in modern computers is divided by byte (byte), in theory, it seems that access to any type of variable can start from any address, but the reality is that when accessing a particular type of variable, it is often accessed at a specific memory address. This requires that the various types of data be arranged in the memory space according to certain rules, rather than the sequential one-to-one emissions, which is the alignment.
Two What principle does the compiler follow to align memory bytes
1. Self-aligning values for data types
The self-aligning value of the data type: The number of bytes it occupies in memory, such as 32-bit systems, char is 1 bytes, short is 2 bytes, int, float, double, long is 4 bytes.
2. Self-aligning values for a struct or class
The self-aligning value of a struct or class: The value that is the largest of its members in its own alignment.
3. Default alignment values
The default alignment value for a struct or class is the value that is the largest of its members in its own alignment value.
4. Specify the Alignment value
The value specified when #pragma pack (value) value.
5. Valid alignment values for structs and classes
The valid alignment values for structs and classes are: The minimum value for the default alignment value and its own alignment value when no alignment value is specified, and the minimum value for the specified alignment value and self-aligning value when the alignment value is specified.
Three How to modify the compiler's default alignment values
When coding, you can modify this dynamically:
To modify the default alignment value, specify a new memory alignment value: #pragma pack (value)
Cancels the specified memory alignment value, restoring the default alignment value: #pragma pack ()
Four How to align memory addresses
For a struct, not only the memory address alignment of each member variable, but also the structure itself is aligned, the rule is: in the assumption that the starting address of the struct is 0x0000, the starting address of each member variable is required to be an integer multiple of its corresponding valid alignment value, It is also required that the size of the struct is an integer multiple of the effective alignment value of the struct body.
Example
Example one:
struct A
{
int A;
Char b;
Short C;
};
The number of bytes of memory that the struct represents is sizeof (struct A) = 8;
The specific analysis is as follows:
Therefore, the valid alignment value of the struct and its member variables is the minimum value of the compiler's default alignment value and its own alignment value:
The self-aligning value of int A is 4
The self-aligning value of char B is 1
The self-aligning value of short C is 2
The self-aligning value of struct A is max (4,1,2) = 4
The compiler default alignment value is the maximum value in each member variable's own alignment value, which is 4.
Therefore, the valid alignment value of int A is min (+) = 4
The valid alignment value of char b is min (1,4) = 1
The valid alignment value of short C is min (2,4) = 2;
The valid alignment value for struct A is min (+) = 4
Assume that struct a starts discharging from the address space 0x0000. The above calculation shows that the first member variable int A has a valid alignment value of 4, so its address is 0x0000~0x0003, and its starting address 0x0000 conforms to 0x0000%4 = 0, the second member variable char b has a valid alignment value of 1, So its storage address is 0x00004, and its starting address 0x0004 conforms to 0x0004%1 = 0, the third member variable short C has a valid alignment value of 2, so its storage address should be 0x0006~0x0007, and its starting address 0x0006 also conform to 0x0006%2 = 0;
Finally, the entire structure of the memory address alignment: To ensure that the entire structure of the size of its effective alignment value of the integer times, and the structure of the effective alignment value of 4, now three member variables have occupied 0x0000~0x0007 a total of 8 bytes of memory space, and 8%4 = 0 also meet the alignment requirements, So the size of the whole structure should be 8.
Example Two
#pargma Pack (8)
struct A
{
Short A; Valid alignment value is min (2,8) = 2
Long B; The valid alignment value is min (4,8) = 4;
};
struct B
{
char c; Valid alignment value is min (1,8) = 1
struct B s; Valid alignment value is min (4,8) = 4
Short E; Valid alignment value is min (2,8) = 2
};
#pargma Pack ()
The values of sizeof (struct A) and sizeof (struct B) are calculated separately.
Similar example one can be analyzed:
sizeof (struct A) = 8,sizeof (struct B) = 16