These questions may be a little vague for many friends, so this article tries to explore the secrets behind them.
First of all, there is at least one thing to be sure that ANSI C guarantees that the positions of the fields in the structure are incremented in memory as they are declared, and that the first address of a field equals the first address of an entire struct instance. For example, there is a structural body:
Copy Code code as follows:
struct Vector{int x,y,z;} s;
int *p,*q,*r;
struct vector *ps;
p = &s.x;
Q = &s.y;
r = &s.z;
PS = &s;
ASSERT (P < q);
ASSERT (P < R);
ASSERT (Q < R);
ASSERT ((int*) PS = = p);
This assertion must not fail
At this point, a friend may ask: "Does the standard stipulate that adjacent fields are also adjacent in memory?" Well, I'm sorry, ANSI C does not guarantee that your program should not rely on this assumption at all times. Does that mean we will never be able to sketch out a clearer and more precise structure in which to store the layout? Oh, of course not. But let's take a moment out of this problem and focus on another important issue ———— memory alignment.
Many actual computer systems have limited location in memory for basic type data, they require that the value of the first address of the data be a multiple of a number k (usually 4 or 8), which is called the alignment of the memory, and this k is called the Zimo number of the data type (alignment modulus). When the ratio of the pair of Zimo number of a type S to the Zimo number of the other type T is an integer greater than 1, we say that the alignment of the type S is stronger than T (strict), and that T is weaker than s (loose). This mandatory requirement simplifies the design of the transfer system between the processor and the memory, and can increase the speed at which the data is read. such as a processor, every time it reads and writes memory from a 8 times-fold address, read or write 8 bytes of data, if the software can ensure that the double type of data starting from 8 times times the address, then read or write a double type of data requires only one memory operation. Otherwise, we may need two memory operations to complete this action, because the data may be just across two 8-byte chunks of memory that match the alignment requirements. Some processors may fail if the data does not meet the alignment requirements, but the processor of the Intel IA32 architecture works correctly regardless of whether the data is aligned. But Intel advises you that if you want to improve performance, all program data should be aligned as much as possible. The Microsoft C compiler (Cl.exe for 80x86) under the Win32 platform uses the following alignment rule by default: The Zimo number of any basic data type T is the size of T, that is, sizeof (t). For example, for the double type (8 bytes), the address of the type data is always a multiple of 8, while char type data (1 bytes) can start at any one address. Under Linux, GCC pursues another set of rules (found in the data, not validated, such as errors): Any 2 byte size (including Single-byte?) The number of pairs of data types (for example, short) is 2, while all other data types (such as long,double) that exceed 2 bytes are Zimo to 4 Zimo.
Now go back to the struct we care about. ANSI c stipulates that the size of a struct type is the sum of the size of all its fields and the size of the fill area between fields or the tail of a field. Well? Padding area? Yes, this is the extra space allocated to the structure body for the purpose of satisfying the memory alignment requirements of the structure body field. So what is the alignment requirement for the structure itself? Yes, the ANSI C standard stipulates that the alignment requirements for struct types cannot be more stringent than the one that is most stringent in all of its fields (but this is not mandatory and VC7.1 is just as strict as they are). Let's take a look at an example (the environment for all of the tests below is Intel Celeron 2.4G + WIN2000 PRO + vc7.1, the memory alignment compilation option is "Default", that is, the/zp and/pack options are not specified):
Copy Code code as follows:
typedef struct MS1
{
Char A;
int b;
} MS1;
Suppose MS1 the memory layout in the following way (the memory address in all the diagrams in this article is incremented from left to right):
_____________________________
| A | B |
+---------------------------+
Bytes:1 4
Because the strongest alignment in MS1 requires a B field (int), the first address of the MS1 object must be a multiple of 4 (the number of Zimo of type int), depending on the compiler's alignment rules and ANSI C standard. Can the B field in the above memory layout satisfy the alignment requirement of type int? Well, of course not. If you are a compiler, how can you skillfully arrange to meet the CPU's addiction? Oh, after 1 milliseconds of hard thinking, you must have come up with the following plan:
_______________________________________
| |///////////| |
| A |//padding//| B |
| |///////////| |
+-------------------------------------+
Bytes:1 3 4
This scheme allocates more than 3 padding (padding) bytes between A and b so that when the entire struct object's first address satisfies the 4-byte alignment requirement, the B field must also satisfy the 4-byte alignment of Int. Then sizeof (MS1) should obviously be 8, whereas the B field is 4 less than the first address of the structure body. Very well understood, isn't it? Now we'll swap the fields in the MS1 for the order:
Copy Code code as follows:
typedef struct MS2
{
int A;
Char b;
} MS2;
Perhaps you think MS2 is simpler than MS1, and its layout should be
_______________________
| A | B |
+---------------------+
Bytes:4 1
Because the MS2 object also satisfies the 4-byte alignment rule, when A's address is equal to the first address of the struct, it must also be 4-byte aligned. Well, the analysis makes sense, but it's not comprehensive. Let's consider what the problem is with defining an array of MS2 types. The C standard guarantees that the size of an array of any type (including a custom struct type) must be equal to the size of a single data of that type multiplied by the number of elements in the array. In other words, there is no space between the elements of the array. According to the above scheme, the layout of an array of MS2 arrays is:
|<- array[1] ->|<- array[2] ->|<-array[3] ...
__________________________________________________________
| a | b | a | b | .......
+----------------------------------------------------------
bytes: 4 1 4 1
When the first address of an array is 4 bytes aligned, array[1].a is also 4 bytes aligned, but what about ARRAY[2].A? Array[3].a ..... It? This scenario can be seen when defining an array of structures so that the fields of all elements in an array are not satisfied with the alignment rules, and must be modified to the following form:
___________________________________
| | |///////////|
| A | b |//padding//|
| | |///////////|
+---------------------------------+
Bytes:4 1 3
Now whether you are defining a single MS2 variable or a MS2 array, you can guarantee that all the fields of all the elements meet the alignment rule. So sizeof (MS2) is still 8, and A's offset to 0,b is 4.
OK, now that you have mastered the basic principles of the structure's internal storage layout, try to analyze a slightly more complex type.
Copy Code code as follows:
typedef struct MS3
{
Char A;
Short B;
Double C;
} MS3;
I'm sure you can come up with the following correct layout diagram:
Padding
_____v_________________________________
| |/| |/////////| |
| A |/| b |/padding/| C |
| |/| |/////////| |
+-------------------------------------+
Bytes:1 1 2 4 8
sizeof (short) equals the 2,b field should start with an even address, so a byte is populated after a, and sizeof (double) equals 8,c field to start at 8 times times the address, preceded by a, b field plus padding byte already has 4 bytes, So after B padding 4 bytes, you can guarantee the alignment requirements for the C field. sizeof (MS3) is equal to 16,b offset is the 2,c offset is 8. Then look at the field in the structure or the structure type:
Copy Code code as follows:
typedef struct MS4
{
Char A;
MS3 b;
} MS4;
The most restrictive field in MS3 is C, so the number of Zimo for the MS3 type data is the same as double (8), and the A field should be populated with 7 bytes, so the MS4 layout should be:
_______________________________________
| |///////////| |
| A |//padding//| B |
| |///////////| |
+-------------------------------------+
Bytes:1 7 16
Obviously, the sizeof (MS4) equals the 24,b offset equals 8.
In actual development, we can change the compiler's alignment rules by specifying the/ZP compilation option. For example, specifying/ZPN (VC7.1 n can be 1, 2, 4, 8, 16) tells the compiler that the maximum number of Zimo is n. In this case, the alignment rules for all basic data types less than or equal to n are the same as the default, but the number of Zimo for data types greater than n bytes is limited to n. In fact, VC7.1 's default to alignment is equivalent to/ZP8. Take a closer look at MSDN's description of this option, and notice that it warns programmers not to use the/ZP1 and/ZP2 options on MIPS and Alpha platforms, or to specify/ZP4 and/ZP8 on 16-bit platforms. )。 Changing the alignment options of the compiler, it is a good review to analyze the memory layout of the above 4 structures against the results of the program operation.
Here, we can answer the last question raised in this article. The memory layout of the structure depends on the CPU, operating system, compiler, and compile-time alignment options, and your program may need to run on multiple platforms, your source code may have to be compiled by different people with different compilers (imagine you provide someone with an open source library), so unless absolutely necessary, Otherwise your program will never rely on these weird memory layouts. By the way, if two modules in a program are compiled separately with different alignment options, it is likely to produce some very subtle errors. If your program does have a very difficult to understand behavior, do not check each module's compilation options carefully.
Study questions: Please analyze the memory layout of the following structures on your platform and try to find a way to arrange the order of field declarations to save memory space as much as possible.
Copy Code code as follows:
A. struct P1 {int A; char b; int c; char D;};
B. struct P2 {int A; char b; char c; int D;};
C. struct P3 {short a[3]; char b[3];
D. struct P4 {short a[3]; char *b[3];
E. struct P5 {struct P2 *a; char b; struct P1 a[2]; };