Converted from bigloomy csdn blog: http://blog.csdn.net/bigloomy/article/details/6633008
Study Notes (in conjunction with the previous article "C/C ++ drill-down questions, specifically sizeof ):
1. #CodeAlignment valueTake the smaller value in the specified value and default value.;
2.According to the compilation results in tc2.0, the single-byte alignment method is used by default, that is, null bytes are not automatically filled.;
3. During GCC compilation, sizeof's void operation result is 1, but it cannot be compiled under vc6.0;
4. Test results: in VC 6.0, the structMembers of the same type of bit domains are relatively compact. alignment is considered for different types.;
For example
Typedef struct {INTA: 1; intb: 4; charc: 4;} mystruct; // The length is 8 typedef struct {INTA: 1; intb: 4; intc: 4 ;} mystruct; // The length is 4 typedef struct {chara: 1; charb: 4; charc: 4;} mystruct; // The length is 2
While tc2.0Only supports bit division for int and unsigned int types;
The original text is as follows:
Many readers may ask, is it necessary to write a blog separately for byte alignment? I think it is necessary, but it is a key point ignored by many people. So what are the functions and causes of byte alignment? Since the processing of buckets between hardware platforms is very different, some platforms can only access certain types of data from some specific addresses, for example, CPU requirements of some architectures must ensure byte alignment during programming. Otherwise, an error occurs when accessing a variable without byte alignment. Some platforms may not do this, but the general situation is that if we do not align the data storage according to the requirements of the appropriate platform during programming, it will cause a loss of access efficiency. For example, some platforms start from the even address each time they read data. For example, if we operate on an int type data, if it is stored at the beginning of the even address, a read cycle can be read, if the data is stored at the beginning of the odd address, it may take two read cycles. The Bytes read from the two cycles must be pieced together to obtain the int type data, as a result, our reading efficiency is low, which also reflects a problem, that is, we often sacrifice space to save time.
Maybe you still don't quite understand the above explanation, so let's take a look at what is byte alignment? In our computer, the memory space is divided by byte. Theoretically, it seems that access to any type of variables can start from any address. However, it is worth noting that, in actual situations, access to specific variables is often performed at specific memory addresses, which requires various types of data to be arranged in space according to certain rules, instead of sequential emissions, this is alignment.
According to the pre-planned arrangement, this time it should be written in "linked list of small secrets of C language (3)", but I found that if I started to explain the Linux kernel linked list directly, in some cases, if we do not give a proper explanation here, some readers may seem hard to understand, so we can pick out byte alignment and write another blog, I will try my best to explain the content about byte alignment here. I hope my explanation will be helpful to you.
Before that, we have to mention the sizeof operator, which is used to return the memory bytes occupied by an object or type. Why don't we call it the sizeof () function here? Take a look at the following code:
View plain
1. # include<Stdio. h>
2.
3. Void print ()
4 .{
5. printf ("Hello world! \ N ");
6. return;
7 .}
8. Void main ()
9 .{
10. printf ("% d \ n", sizeof (print ()));
11. return;
12 .}
In Linux, there is no problem in using gcc for compiling this Code. For the void type, its length is 1, however, if we run it under vc6, the illegal sizeof operand error will occur. Therefore, we call it an operator more accurate. Since it is an operator, let's look at its usage methods: [lgz1]
1. sizeof (object); // sizeof (object );
2. sizeof (type_name); // sizeof (type );
3. sizeofobject; // sizeof object; this method is usually not used in the code, so it is rarely seen.
Let's take a look at the code below to deepen my impression:
View plain
1. # include<Stdio. h>
2.
3. Void main ()
4 .{
5. Int I;
6. printf ("sizeof (I): \ t % d \ n", sizeof (I ));
7. printf ("sizeof (4): \ t % d \ n", sizeof (4 ));
8. printf ("sizeof (4 + 2.5): \ t % d \ n", sizeof (4 + 2.5 ));
9. printf ("sizeof (INT): \ t % d \ n", sizeof (INT ));
10. printf ("sizeof 5: \ t % d \ n", sizeof 5 );
11. return;
12 .}
The running result is:
View plain
1. sizeof (I): 4
2. sizeof (4): 4
3. sizeof (4 + 2.5): 8
4. sizeof (INT): 4
5. sizeof 5: 4
6. Press any key to continue
We can see from the running results that the above usage methods, in fact, sizeof calculation object size is also converted to the calculation of the object type, that is, the sizeof values of different objects of the same type are the same. From the code given, we can also see that sizeof can evaluate an expression. The Compiler determines the size based on the final result type of the expression, however, it generally does not calculate the expression or does not execute the function body when the expression is a function. For example:
View plain
1. # include<Stdio. h>
2. Int print ()
3 .{
4. printf ("Hello bigloomy! ");
5. Return 0;
6 .}
7. Void main ()
8 .{
9. printf ("sizeof (print (): \ t % d \ n", sizeof (print ()));
10. return;
11 .}
The running result is:
View plain
1. sizeof (print (): 4
2. Press any key to continue
From the results, we can see that the print () function is not called.
Next let's take a look at a macro in the Linux Kernel linked list:
# Defineoffsetof (type, member) (size_t) & (type *) 0)-> Member)
The macro can be explained in the following four steps:
1. (type *) 0) 0 the address is forced to "convert" to a pointer of the type structure type;
2. (type *) 0)-> member accesses the member data member in the type structure;
3. & (type *) 0)-> member) extracts the address of the data member in the type structure;
4. (size_t) (& (type *) 0)-> member) The result is converted to the size_t type.
The clever feature of macro offsetof is to forcibly convert the 0 address to a pointer of the type structure type. If the type structure uses the first address 0 of the memory space as the starting address, the Member Address is naturally an offset address. Some readers may wonder if they have to use 0? Of course not. We just want to make the computation simple. It can also be used as its value, except that the calculated result minus this value is the offset address. Let's take a look at the following code:
View plain
1. # include <stdio. h>
2.
3. # define offsetof (type, member) (size_t) & (type *) 4)-> Member)
4.
5.Typedef structStu1
6 .{
7.IntA;
8.IntB;
9.} stu1;
10.
11.VoidMain ()
12 .{
13. printf ("offsetof (stu1, A): \ t % d \ n", offsetof (stu1, A)-4 );
14. printf ("offsetof (stu1, B): \ t % d \ n", offsetof (stu1, B)-4 );
15 .}
The running result is:
View plain
1. offsetof (stu1, A): 0
2. offsetof (stu1, B): 4
3. Press any keyContinue
To make readers more impressed, we didn't use 0 in the code here, but used 4. So the offset address is subtracted from the final result, of course, we use 0 in actual use.
After understanding the above macro offsetof, let's take a look at the following code:
View plain
1. # include <stdio. h>
2.
3. # define offsetof (type, member) (size_t) & (type *) 0)-> Member)
4.
5.Typedef structStu1
6 .{
7.IntA;
8.CharB [1];
9.IntC;
10.} stu1;
11.
12.VoidMain ()
13 .{
14. printf ("offsetof (stu1, A): \ t % d \ n", offsetof (stu1, ));
15. printf ("offsetof (stu1, B): \ t % d \ n", offsetof (stu1, B ));
16. printf ("offsetof (stu1, c): \ t % d \ n", offsetof (stu1, c ));
17. printf ("sizeof (stu1): \ t % d \ n ",Sizeof(Stu1 ));
18 .}
The running result is:
View plain
1. offsetof (stu1, A): 0
2. offsetof (stu1, B): 4
3. offsetof (stu1, c): 8
4.Sizeof(Stu1): 12
5. Press any keyContinue
Readers who do not know about byte alignment may wonder how the offset of C is 8 and the size of the struct is 12? Because it is sizeof (INT) + sizeof (char) + sizeof (INT) = 9. In fact, this is a special processing of variable storage by the compiler. To improve the CPU storage speed, the compiler performs alignment on the starting addresses of some variables. By default, the compiler specifies that the offset of the starting address of each member variable to the starting address of the structure must be a multiple of the bytes occupied by the variable type. Now let's analyze the above Code. If we assume that the starting address of a is 0 and it occupies 4 bytes, then the idle address is 4, which is a multiple of 1, the starting address of B is 4, which occupies one byte. The following idle address is 5, while C is the int variable, which occupies 4 bytes. 5 is not an integer multiple of 4, so move backward, locate the nearest 8 to 5 as the starting address for storing C, C also occupies 4 bytes, so the final size of the struct is 12. Now let's take a look at the following code:
View plain
1. # include <stdio. h>
2.
3.Typedef structStu1
4 .{
5.CharArray [7];
6.} stu1;
7.
8.Typedef structStu2
9 .{
10.DoubleFa;
11.} stu2;
12.
13.Typedef structStu3
14 .{
15. stu1 S;
16.CharSTR;
17.} stu3;
18.
19.Typedef structStu4
20 .{
21. stu2 S;
22.CharSTR;
23.} stu4;
24.
25.VoidMain ()
26 .{
27. printf ("sizeof (stu1): \ t % d \ n ",Sizeof(Stu1 ));
28. printf ("sizeof (stu2): \ t % d \ n ",Sizeof(Stu2 ));
29. printf ("sizeof (stu3): \ t % d \ n ",Sizeof(Stu3 ));
30. printf ("sizeof (stu4): \ t % d \ n ",Sizeof(Stu4 ));
31 .}
The running result is:
View plain
1.Sizeof(Stu1): 7
2.Sizeof(Stu2): 8
3.Sizeof(Stu3): 8
4.Sizeof(Stu4): 16
5. Press any keyContinue
Analyze the above running results, focusing on struct stu3 and struct stu4,In struct stu3, a byte alignment is used, because both stu1 and stu3 have only one char type. In struct stu3, we define a stu1 type S, stu1 occupies 7, so with the next byte STR added, sizeof (stu3) is 8. In stu4, because we define a stu2 type S, and S is a double type variable, which occupies 8 bytes, so what we use in stu4 is 8 bytes alignment.. If we assume that s in stu4 is stored from address 0 and takes 8 bytes, the next idle address is 8. According to the above explanation, we can just store STR here. So after all the variables are allocated space, stu4 struct occupies 9 bytes, but 9 is not the struct boundary number, that is to say, we need to allocate an integer multiple of the number of bytes occupied by the type that occupies the largest space in the struct, which is an integer multiple of 8 bytes occupied by the double type, therefore, we need to allocate seven bytes of space. The space of the seven bytes is not used and is automatically filled by the compiler, without storing anything meaningful.
Of course, we can also use the pre-compiled command # pragma pack (value) [lgz2] To tell the compiler to replace the default value with the specified alignment value. Next let's look at a piece of code.
View plain
1. # include <stdio. h>
2.
3. # pragma pack (1)/* specify to align by 1 byte */
4.
5.Typedef UnionStu1
6 .{
7.CharSTR [10];
8.IntB;
9.} stu1;
10.
11. # pragma pack ()/* cancel the specified alignment and restore the default alignment */
12.
13.Typedef UnionStu2
14 .{
15.CharSTR [10];
16.IntB;
17.} stu2;
18.
19.VoidMain ()
20 .{
21. printf ("sizeof (stu1): \ t % d \ n ",Sizeof(Stu1 ));
22. printf ("sizeof (stu2): \ t % d \ n ",Sizeof(Stu2 ));
23 .}
The running result is:
View plain
1.Sizeof(Stu1): 10
2.Sizeof(Stu2): 12
3. Press any keyContinue
Now let's analyze the above Code. We have been using struct all the time before, so here we specially illustrate a Union code for analysis, we all know that the size of Union depends on the size of one of the most occupied members. Because we use 1-byte alignment in Union stu1, the largest space occupied by stu1 is the char STR [10] array with a value of 10. Why is stu1 10 while stu2 12? # Pragma pack () is used in stu2 to cancel the specified alignment and restore the default alignment. So because of the existence of stu2 members of the int type, the alignment of stu2 is changed to 4-byte alignment. That is to say, the size of stu2 must be 4 to the world, in other words, the size of stu2 is an integer multiple of 4, so the occupied space is changed to 12.