Memory Alignment
1. Concept
Alignment is related to the location of the data in memory. If the memory address of a variable is exactly the integer multiple of its length, he is called natural alignment. For example, under a 32-bit CPU, assuming an integer variable has an address of 0x00000004, it is naturally aligned.
1.1 Why byte alignment
The root cause of the need for byte alignment is the efficiency of CPU access data. Assuming that the address of the integer variable above is not naturally aligned, such as 0x00000002, then the CPU needs to access two memory if it takes its value, and the first time it takes a short from 0x00000002-0x00000003 The second takes a short from 0x00000004-0x00000005 and then combines the desired data, and if the variable is on the 0x00000003 address, it accesses three memory, the first char, the second is short, and the third is char, Then combine to get the integer data. If the variable is in the natural alignment position, the data can be fetched once. Some systems have strict alignment requirements, such as SPARC systems, where an error occurs if an unaligned data is taken, as an example:
Char Ch[8];
Char *p = &ch[1];
int i = * (int *) p;
The runtime will report segment error, and there will be no errors on the x86, just a decrease in efficiency. 1.2 Correct handling of byte alignment
For a standard data type, its address is as long as it is an integer multiple of its length, and non-standard data types are aligned according to the following principle:
Arrays: aligned According to the basic data type, the first one is aligned with the nature of the back.
Union: Aligns by the data type that contains the largest number of lengths.
struct: Each data type in the struct is aligned.
For example, there is a structure like this:
struct stu{
char sex;
int length;
Char name[10];
};
struct Stu My_stu;
Since GCC defaults to 4-byte alignment under x86, it will be padded with three and two bytes after sex followed by the name to align length with the entire structure. So our sizeof (MY_STU) will get a length of 20, not 15.
1.3__attribute__ Options
We can compile the program according to the alignment size we set, and GNU uses the __ATTRIBUTE__ option to set it, for example, we want to align the structure just one byte, so we can define the structure
struct stu{
char sex;
int length;
Char name[10];
}__ATTRIBUTE__ ((Aligned (1)));
struct Stu My_stu;
Then sizeof (MY_STU) can get a size of 15.
The definition above is equivalent to
struct stu{
char sex;
int length;
Char name[10];
}__ATTRIBUTE__ ((packed));
struct Stu My_stu;
The __attribute__ ((packed)) variable or struct member uses the smallest alignment, that is, the variable is a byte-aligned, and the field (field) is bit-aligned.
1.4 When to set the alignment
When designing communication protocols for different CPUs, or the structure of the registers when writing hardware drivers, these two places need to be aligned by one byte. Even if it seems to be naturally aligned, it should be aligned so that different compiler-generated code is not the same.
2. Quick Understanding 2.1. What is byte alignment
In C, a struct is a composite data type whose constituent elements can be variables of basic data types (such as int, long, float, and so on) or data units of composite data types (such as arrays, structs, unions, etc.). In structs, the compiler allocates space for each member of the structure according to its natural boundary (alignment). Each member is 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.
In order for the CPU to be able to quickly access variables, the starting address of the variable should have some characteristics, called "alignment". For example, a 4-byte int, whose starting address should be on a 4-byte boundary, where the starting address can be divisible by 4.
2.2. What is the effect of byte alignment
Byte alignment is not only convenient for fast CPU access, but also a reasonable use of byte alignment can effectively save storage space.
For a 32-bit machine, 4-byte alignment can increase the speed of the CPU access, such as a long variable, if the 4-byte boundary is stored, then the CPU reads two times, so the efficiency is low. However, using a 1-byte or 2-byte alignment in a 32-bit machine can cause variable access speed to be reduced. So this takes into account the processor type, and also the compiler type. The default is 4-byte alignment in VC, and GNU GCC is also the default 4-byte alignment.
2.3. Change the default byte alignment of the C compiler
By default, the C compiler allocates space for each variable or data unit according to its natural boundary conditions. In general, the default boundary conditions can be changed by the following method:
• Using pseudo-directives #pragma pack (n), the C compiler will be aligned in N bytes.
• Use pseudo-directives #pragma pack () to cancel custom byte alignment.
In addition, there are one of the following ways:
__attribute ((aligned (n))) that aligns the members of the structure to the N-byte natural boundary. If the length of a member in the structure is greater than n, the length of the maximum member is aligned.
__attribute__ ((packed)), cancels the optimization alignment of the structure during compilation, aligned according to the actual number of bytes consumed.
2.4. Illustrative examples
Example 1
struct test
{
Char X1;
Short X2;
Float X3;
Char x4;
};
Because the compiler makes a natural boundary for this struct by default (some say "natural-to-bounded" I think the boundary is more smooth) aligned, the first member of the struct is x1, with an offset address of 0, which 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 boundary address, 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 boundary element required by all members of the struct, and thus the natural bounds of the test structure is 4 bytes, and the compiler populates 3 empty bytes after the member X4. The entire structure occupies 12 bytes of space.
Example 2
#pragma pack (1)//Let the compiler make 1-byte alignment for this structure
struct test
{
Char X1;
Short X2;
Float X3;
Char x4;
};
#pragma pack ()//Cancel 1-byte alignment, revert to default 4-byte alignment
At this time, the value of sizeof (struct test) is 8.
Example 3
#define Gnuc_packed __attribute__ ((PACKED))
struct PACKED test
{
Char X1;
Short X2;
Float X3;
Char x4;
}gnuc_packed;
The value of sizeof (struct test) is still 8. 3 in-depth understanding 3.1 What is byte alignment
The memory space in modern computers is divided by 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, which requires all types of data to be spatially arranged according to certain rules, Instead of sequentially one by one emissions, that's the alignment.
The effect and reason of alignment: the processing of storage space varies greatly with each hardware platform. Some platforms can only access certain types of data from certain specific addresses. For example, some architectures have an error when accessing a variable that is not aligned, so programming must ensure byte alignment in this architecture. Other platforms may not have this, but the most common is the loss of access efficiency if the data storage is not aligned according to their platform requirements. For example, some platforms read each time from the even address, if an int (assuming 32-bit system) if the location of the place where the even address begins, then a read cycle can be read out of the 32bit, and if it is stored at the beginning of the odd address, it takes 2 read cycles, The 32bit data can be obtained by piecing together the high and low bytes of the two read-out results. Obviously, the reading efficiency is much lower. effect of 3.2-byte alignment on programs:
Let's take a look at some examples (32bit,x86 environment, GCC compiler):
The structure is defined as follows:
struct A
{
int A;
Char b;
Short C;
};
struct B
{
Char b;
int A;
Short C;
};
The lengths of the various data types on the 32-bit machine are now known as:
Char:1 (Signed and unsigned)
Short:2 (Signed and unsigned)
Int:4 (Signed and unsigned)
Long:4 (Signed and unsigned)
Float:4 Double:8
So what's the size of the top two structures?
The result is:
sizeof (Strcut A) value is 8
sizeof (struct B) has a value of 12.
struct A contains 4-byte-length int one, 1-byte-length char one and 2-byte length of short data one, B is the same;
The result above is because the compiler wants to align the data members spatially. The above is the result of the alignment according to the compiler's default settings, then we can not change the compiler's default alignment settings, of course. For example:
#pragma pack (2)/* Specify 2-byte alignment */
struct C
{
Char b;
int A;
Short C;
};
#pragma pack ()/* To cancel the specified alignment, restore the default alignment */
The sizeof (struct C) value is 8.
Modify the alignment value to 1:
#pragma pack (1)/* Specify 1-byte alignment */
struct D
{
Char b;
int A;
Short C;
};
#pragma pack ()/* To cancel the specified alignment, restore the default alignment */
The sizeof (struct D) value is 7.
Later we explain the role of #pragma pack (). 3.3 How the compiler is aligned according to the principle
Let's start by looking at four important basic concepts:
1. The alignment value of the data type itself:
For char type data, its own alignment value is 1, for the short type is 2, for the int,float,double type, its own alignment value is 4, the unit byte.
2. The self-aligning value of a struct or class: The value that is the largest of its members in its own alignment value.
3. Specify the alignment value: The specified alignment value when #pragma pack (value) is values.
4. Valid alignment values for data members, structs, and classes: The value that is small for its own alignment value and for the specified alignment value.
With these values, we can easily discuss the members of a specific data structure and its own alignment. The valid alignment value n is the value that is ultimately used to determine how the data is stored, most importantly. A valid alignment of n is the "alignment on n", which means that the data "holds the starting address%n=0". Data variables in the structure are emitted in a defined order of precedence. The starting address of the first data variable is the starting address of the structure. The member variables of the struct should be aligned with the emission, and the structure itself should be rounded according to its own valid alignment value (that is, the total length of the struct member variable is required to be an integral multiple of the effective alignment value of the struct, as the following example understands). So you don't understand the values of the above examples.
Example Analysis:
analysis example B;
struct B
{
Char b;
int A;
Short C;
};
Suppose B starts discharging from the address space 0x0000. In this example, the specified alignment value is not defined, and in the author's environment, the value defaults to 4. The first member variable B has its own alignment value of 1, which is smaller than the specified or default alignment value of 4, so its valid alignment value is 1, so its storage address 0x0000 conforms to 0x0000%1=0. The second member variable A has its own alignment value of 4, so the valid alignment value is also 4. So it can only be stored in the starting address of 0x0004 to 0x0007 four consecutive byte space, review 0x0004%4=0, and immediately close to the first variable. The third variable, C, has its own alignment value of 2, so the valid alignment value is also 2, which can be stored in the two byte space of 0x0008 to 0x0009, conforming to 0x0008%2=0. So everything from 0x0000 to 0x0009 is stored in B content. Then look at the data structure B's own alignment value for its variable maximum alignment value (here is B) so is 4, so the structure of the effective alignment value is also 4. 0x0009 to 0x0000=10 bytes, (10+2)%4=0 according to the requirements of the structural rounding. So 0x0000a to 0x000b is also occupied by struct B. So B has a total of 12 bytes from 0x0000 to 0x000b, sizeof (struct B) = 12; In fact, if this is the case it has aligned the satisfied byte, because its starting address is 0, so it must be aligned, the reason is to add 2 bytes later, Because the compiler is in order to achieve the access efficiency of the array of structures, imagine if we define an array of struct B, then the first struct starts with a 0 no problem, but the second structure? As defined by the array, all elements in the array are next to each other, and if we do not add the size of the structure to the integer multiples of 4, Then the starting address of the next structure will be 0x0000a, which obviously does not satisfy the structure's address alignment, so we want to add the structure to an integer multiple of the effective alignment size. In fact, for char type data, its own alignment value is 1, for the short type is 2, for Int,float, A double type with its own alignment value of 4, these existing types of self-aligning values are also based on arrays, only because the lengths of these types are known, so their own alignment values are known.
Similarly, analyze the above example C:
#pragma pack (2)/* Specify 2-byte alignment */
struct C
{
Char b;
int A;
Short C;
};
#pragma pack ()/* To cancel the specified alignment, restore the default alignment */
The first variable B has its own alignment value of 1, the specified alignment value is 2, so its valid alignment value is 1, assuming that C starts from 0x0000, then B is stored in 0x0000, conforms to 0x0000%1=0, the second variable has its own alignment value of 4, and the alignment value is 2, so the valid alignment value is 2. So the order is stored in 0x0002, 0x0003, 0x0004, 0x0005 four consecutive bytes, in accordance with 0x0002%2=0. The third variable, C, has its own alignment value of 2, so the valid alignment value is 2, which is stored sequentially
In 0x0006, 0x0007, in accordance with 0x0006%2=0. So from 0x0000 to 0x00007 a total of eight bytes is stored in the C variable. and C has its own alignment value of 4, so the valid alignment value for C is 2. And 8%2=0,c only takes up eight bytes of 0x0000 to 0x0007. So sizeof (struct C) =8. 3.4 How to modify the compiler's default alignment values
1. In the VC IDE, you can modify this: [project]| [settings],c/c++ tab category of the codegeneration option of the struct Member alignment modified, the default is 8 bytes.
2. When encoding, you can change this dynamically: #pragmapack. Note: It is pragma instead of progma. 3.5 for byte alignment, what we consider in programming
If you want to consider saving space when programming, then we only need to assume that the first address of the structure is 0, then the various variables are arranged according to the above principles, the basic principle is to make the variables in the structure from small to large declaration of the type size, Minimize the amount of space in the middle. There is another way to make space for the efficiency of time, we show the space to be filled to align, such as: There is a space-time approach is to explicitly insert reserved members:
struct a{
Char A;
Char reserved[3];//use space to change time
int b;
}
The reserved member has no meaning to our program, it just fills the space to achieve byte alignment, and of course even without this member the compiler will automatically fill us with the alignment, and we add it just to play an explicit reminder. potential pitfalls of 3.6-byte alignment
Many of the pitfalls of alignment in code are implicit. For example, when forcing type conversions. For example:
unsigned int i = 0x12345678;
unsigned char *p=null;
unsigned short *p1=null;
p=&i;
*p=0x00;
p1= (unsigned short *) (p+1);
*p1=0x0000;
The last two lines of code, from the odd boundary to access the Unsignedshort variable, clearly do not conform to the rules of alignment.
On x86, similar operations can only affect efficiency, but on MIPS or SPARC, it can be an error because they require byte alignment.
3.7 How to find problems with byte alignment
If an alignment or assignment problem occurs, first view
1. Compiler's big little side settings
2. See if the system itself supports non-aligned access
3. If the support depends on whether the alignment is set or not, if there is no access, you need to add some special decorations to flag its special access operation
Example:
#include <stdio.h>
Main ()
{
struct A {
int A;
Char b;
Short C;
};
struct B {
Char b;
int A;
Short C;
};
#pragma pack (2)/* Specify 2-byte alignment */
struct C {
Char b;
int A;
Short C;
};
#pragma pack ()/* To cancel the specified alignment, restore the default alignment */
#pragma pack (1)/* Specify 1-byte alignment */
struct D {
Char b;
int A;
Short C;
};
#pragma pack ()/* To cancel the specified alignment, restore the default alignment */
int s1=sizeof (struct A);
int s2=sizeof (struct B);
int s3=sizeof (struct C);
int s4=sizeof (struct D);
printf ("%d\n", S1);
printf ("%d\n", S2);
printf ("%d\n", S3);
printf ("%d\n", S4);
}
Output:
8
12
8
7
To modify the code:
struct A {
int A;
Char b;
Short C;
};
struct B {
Char b;
int A;
Short C;
};
Output:
4
4
The output is 4, which illustrates the alignment of the previous int effect.