In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly explains "how to achieve memory alignment in C language". The content of the explanation in this article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought. Let's study and learn how to achieve memory alignment in C language.
I. concept
Alignment is related to the location of the data in memory. If the memory address of a variable is exactly an integral multiple of its length, it is called natural alignment. For example, under 32-bit cpu, suppose the address of an integer variable is 0x00000004, then it is naturally aligned.
Second, why byte alignment
The fundamental reason for the need for byte alignment is the efficiency of CPU's access to data. Assuming that the address of the integer variable above is not naturally aligned, such as 0x00000002, then CPU needs to access memory twice if it takes its value, the first time it takes a short from 0x00000002-0x00000003, the second time it takes a short from 0x00000004-0x00000005 and then combines it to get the desired data, and if the variable is on the 0x00000003 address, it accesses memory three times, the first time is char, the second time is short, the third time is char, and then the integer data is combined. If the variable is in a naturally aligned position, the data can be fetched only once. Some systems have very strict alignment requirements, such as the sparc system. Errors may occur if you take unaligned data, for example:
Char ch [8]; char * p = & ch [1]; int I = * (int *) p;
Segment error will be reported at run time, but there will be no errors on x86, just inefficient.
Third, correctly handle byte alignment
For a standard data type, its address only needs to be an integral multiple of its length, while the non-standard data type is aligned according to the following principles:
Array: align according to the basic data type, and the first alignment will naturally align the latter.
Union: aligned by the longest data type it contains.
Structure: each data type in the structure should be aligned.
For example, there is a structure as follows:
Struct stu {char sex; int length; char name [10];}; 20 struct stu my_stu
Since GCC is 4 bytes aligned by default under x86, it fills three and two bytes after sex and name, respectively, to align length with the entire structure. So we sizeof (my_stu) will get a length of 20 instead of 15.
IV. _ _ attribute__ option
We can compile the program according to the alignment size set by ourselves, and GNU uses the _ _ attribute__ option to set it. For example, if we want to align the structure to one byte, we can define the structure like this.
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 above definition is equivalent to
Struct stu {char sex; int length; char name [10];} _ attribute__ ((packed)); struct stu my_stu
The variable or structure member of _ _ attribute__ ((packed)) uses minimal alignment, that is, one-byte alignment to the variable and bit alignment to the field.
When do you need to set the alignment
When designing communication protocols under different CPU, or when writing hardware drivers, the structure of registers needs to be aligned by one byte. Even if it looks natural, align it so that different compilers don't generate different code.
First, quick understanding
1. What is byte alignment?
In C language, structure is a compound data type, and its constituent elements can be not only variables of basic data types (such as int, long, float, etc.), but also data units of some compound data types (such as array, structure, union, etc.). In a structure, the compiler allocates space for each member of the structure according to its natural boundary (alignment). Each member is stored in memory in the order in which they are declared, and the address of the first member is the same as that of the entire structure.
In order for CPU to access variables quickly, the starting address of variables should have some characteristics, that is, the so-called "alignment". For example, the starting address of a 4-byte int should be on the 4-byte boundary, that is, the starting address is divisible by 4.
two。 What is the use of byte alignment?
The function of byte alignment is not only to facilitate fast access of cpu, but also to save storage space effectively by reasonable use of byte alignment.
For 32-bit computers, 4-byte alignment can improve cpu access speed, such as a variable of long type, if it is stored across the 4-byte boundary, then the cpu has to be read twice, which is inefficient. However, using 1-byte or 2-byte alignment on a 32-bit machine slows down variable access. So you have to consider the processor type, as well as the compiler type. Vc defaults to 4-byte alignment, and GNU gcc defaults to 4-byte alignment.
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, you can change the default boundary condition in the following ways:
Using the directive # pragma pack (n), the C compiler aligns with n bytes.
Use the directive # pragma pack () to cancel the custom byte alignment.
In addition, there is another way:
_ _ attribute ((aligned (n) to align the acting structure members on the n-byte natural boundary. If there is a member in the structure whose length is greater than n, it is aligned according to the length of the largest member.
_ _ attribute__ ((packed)), cancel the optimized alignment of the structure during compilation, and align according to the actual number of bytes occupied.
4. Give examples to illustrate
Example 1
Struct test {char x1 | short x2 * * float x3 * * char x4;}
Because the compiler by default aligns the struct with a natural boundary (some people say "natural boundary" I think the boundary is easier), the first member of the structure, x1, has an offset address of 0, occupying the first byte. The second member x2 is of type short and its starting address must be 2-byte bounded, so the compiler fills a blank byte between x2 and x1. The third member x3 and the fourth member x4 of the structure fall right on their natural boundary address, and no additional padding bytes are needed in front of them. In test structure, member x3 requires 4-byte bounding, which is the largest boundary unit of all members of the structure, so the natural boundary condition of test structure is 4 bytes, and the compiler fills 3 empty bytes after member x4. The whole structure occupies 12 bytes of space.
Example 2
# pragma pack (1) / / Let the compiler align this structure with 1 byte
Struct test {char x1 position short x 2 position float x 3 × char x 4;}; # pragma pack () / / unalign 1 byte and revert to the default 4 byte alignment
At this point, the value of sizeof (struct test) is 8.
Example 3
# define GNUC_PACKED _ attribute__ ((packed)) struct PACKED test {char x1 to short x2 position float x3 to char x4;} GNUC_PACKED
At this point the value of sizeof (struct test) is still 8.
Second, deep understanding
What is byte alignment and why?
TragicJun was published in 2006-9-18 9:41:00 in modern computers. Memory space is divided according to byte. In theory, it seems that access to any type of variable can start at any address, but in practice, it is often accessed at a specific memory address when accessing specific types of variables, which requires various types of data to be arranged in space according to certain rules, rather than sequential discharge one after another. This is alignment.
The role and reason of alignment: the handling of storage space varies greatly from hardware platform to hardware platform. Some platforms can only access certain types of data from certain addresses. For example, in some architectures, CPU may make an error when accessing an unaligned variable, so programming in this architecture must ensure byte alignment. This may not be the case on other platforms, but the most common is the loss of access efficiency if the data storage is not aligned according to the requirements of the platform. For example, on some platforms, every read starts with the even address. If an int (assuming a 32-bit system) is stored at the beginning of the even address, then the 32bit can be read in one read cycle, while if it is stored at the beginning of the odd address, it takes two read cycles, and the high and low bytes of the results read twice can be pieced together to get the 32bit data. Obviously, the reading efficiency has dropped a lot.
two。 The effect of byte alignment on the program:
Let's first take a look at a few examples (32bit gcc compiler):
Let the structure be defined as follows:
Struct A {int a; char b; short c;}; struct B {char b; int a; short c;}
It is now known that the lengths of various data types on 32-bit machines are as follows:
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 is the size of the above two structures?
The result is:
Sizeof (strcut A) value is 8
The value of sizeof (struct B) is 12.
Structure A contains a 4-byte int, a 1-byte char and a 2-byte char, as well as B, which is supposed to be 7 bytes in size.
The above result occurs because the compiler aligns the data members spatially. The above is the result of alignment according to the default settings of the compiler, so can we change the default alignment settings of the compiler? of course. For example:
# pragma pack (2) / * specify 2-byte alignment * /
Struct C {char b; int a; short c;}
# pragma pack () / * cancel the specified alignment and restore the default alignment * /
The sizeof (struct C) value is 8.
Change the alignment value to 1:
# pragma pack (1) / * specify 1 byte alignment * /
Struct D {char b; int a; short c;}
# pragma pack () / * cancel the specified alignment and restore the default alignment * /
The sizeof (struct D) value is 7.
We will explain the function of # pragma pack () later.
three。 According to what principles is the compiler aligned?
Let's first look at four important basic concepts:
1. The alignment value of the data type itself:
For char data, its self-alignment value is 1, for int,float,double data, it is 2, and for chars data, it is 4, per byte.
two。 The self-aligned value of a structure or class: the value that has the largest self-aligned value among its members.
3. Specifies the alignment value value when specifying the alignment value: # pragma pack (value).
4. Valid alignment values for data members, structures, and classes: the self-alignment value and the value within the specified alignment value.
!!! Sizeof () does not consider the static type variable sizeof only calculates memory on the stack, not in the static datastore
With these values, we can easily discuss the alignment between the members of a specific data structure and itself. The effective alignment value N is the most important value that is ultimately used to determine how the data is stored. Effectively aligning N means "aligning on N", that is, the "storage starting address% Numb0" of the data. The data variables in the data structure are discharged in the order in which they are defined. The starting address of the first data variable is the starting address of the data structure. The member variables of the structure should be aligned and discharged, and the structure itself should be rounded according to its own effective alignment value (that is, the total length occupied by the structure member variable needs to be an integer multiple of the effective alignment value of the structure, combined with the following example). This makes it impossible to understand the values of the above examples.
Example analysis:
Analyze example B
Struct B {char b; int a; short c;}
Suppose B starts to discharge from the address space 0x0000. The specified alignment value is not defined in this example, which defaults to 4 in the author's environment. The self-alignment value of the first member variable b is 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 a self-alignment value of 4, so the valid alignment value is also 4, so it can only be stored in four consecutive bytespaces with the starting address of 0x0004 to 0x0007, double-check 0x0004%4=0, and close to the first variable. The third variable, c, has a self-alignment value of 2, so the valid alignment value is also 2, which can be stored in the two-byte space from 0x0008 to 0x0009, which conforms to 0x0008%2=0. So everything from 0x0000 to 0x0009 is B content. Again, the self-alignment value of data structure B is the largest alignment value of its variables (here is b), so it is 4, so the valid alignment value of structure is also 4. According to the requirement of rounded structure, 0x0009 to 0x0000=10 bytes, (10'2)% 4'0. So 0x0000A to 0x000B are also occupied by structure B. So B has 12 bytes from 0x0000 to 0x000B, sizeof (struct B) = 12 In fact, if as far as this one is concerned, it has aligned the satisfying bytes, because its starting address is 0, so it must be aligned. 2 bytes are added later because the compiler is in order to achieve the access efficiency of the structure array. Imagine if we define an array of structure B, then the first structure starting address is 0, but what about the second structure? According to the definition of the array, all the elements in the array are adjacent to each other. If we do not add the size of the structure to an integer multiple of 4, then the starting address of the next structure will be 0x0000A, which obviously cannot satisfy the address alignment of the structure, so we have to supplement the structure to an integer multiple of the effective alignment size. In fact, such as: for char data, its self-alignment value is 1, for int,float,double data, its self-alignment value is 4, these existing types' self-alignment values are also based on the array, just because the length of these types is known, so their self-alignment values are known.
By the same token, analyze the above example C:
# pragma pack (2) / * specify 2-byte alignment * /
Struct C {char b; int a; short c;}; # pragma pack () / * cancel the specified alignment and restore the default alignment * /
The first variable b has a self-alignment value of 1 and specifies an alignment value of 2, so its valid alignment value is 1. If C starts with 0x0000, then b is stored in 0x0000 and conforms to 0x0000% 1.0. The second variable has a self-alignment value of 4 and specifies an alignment value of 2, so the valid alignment value is 2, so it is sequentially stored in four consecutive bytes of 0x0002, 0x0003, 0x0004 and 0x0005, which conforms to 0x0002%2=0. The third variable c has a self-alignment value of 2, so the valid alignment value is 2, which is stored sequentially.
In 0x0006 and 0x0007, it conforms to 0x0006%2=0. So a total of eight bytes from 0x0000 to 0x00007 stores the variables of C. The self-alignment value of C is 4, so the effective alignment value of C is 2. And 8% 20% 0x0007 C only takes up eight bytes from 0x0000 to 0x0007. So sizeof (struct C) = 8.
four。 How do I modify the default alignment value of the compiler?
1. In VC IDE, you can modify it like this: [Project] | [Settings], which is modified in the Struct Member Alignment of the Code Generation option of the Category Category tab, which defaults to 8 bytes.
two。 When coding, you can dynamically modify it as follows: # pragma pack. Note: it's pragma, not progma.
five。 How do we consider byte alignment in programming?
If space saving is to be considered when programming, then we only need to assume that the first address of the structure is 0, and then the variables are arranged according to the above principles. The basic principle is to declare the variables in the structure according to the type size from small to large, so as to minimize the filling space in the middle. Another is that in order to trade space for time efficiency, we show filling space for alignment. For example, there is a method of using space for time to explicitly insert reserved members:
Struct A {char a; char reserved [3]; / / use space for time int b;}
The reserved member means nothing to our program, it just fills the space to achieve byte alignment, of course, even without this member, the compiler will usually give us automatic filling alignment, and we add it ourselves as an explicit reminder.
six。 Hidden dangers of byte alignment: many of the hidden dangers of alignment in the code are implicit. For example, when casting a type. For example:
Unsigned int i = 0x12345678 / unsigned char * pins / nulls / unsigned short * p1s / nulls / pins / nulls / p1s /
The last two lines of code, accessing unsigned variables from odd boundaries, obviously do not conform to the alignment rules.
On x86, similar operations only affect efficiency, but on MIPS or sparc, it may be an error because they require byte alignment.
seven。 How to find problems with byte alignment:
If there is an alignment or assignment problem, check it first.
1. Big little side settings of the compiler
two。 See if the system itself supports unaligned access.
3. If it is supported, it depends on whether the setting is aligned or not, and if not, you need to add some special modifications to mark its special access operation.
For example:
# include 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 () / * cancel the specified alignment and restore the default alignment * / # pragma pack (1) / * specify 1 byte alignment * / struct D {char b; int a; short c;}; # pragma pack () / * cancel the specified alignment and 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:
Struct A {/ / int a; char b; short c;}; struct B {char b; / / int a; short c;}; 81287
Modify the code: output:
four
four
The output is all 4, indicating that the previous int affects the alignment!
Just look at the picture and understand.
Thank you for your reading, the above is the content of "how to achieve memory alignment in C language". After the study of this article, I believe you have a deeper understanding of how to achieve memory alignment in C language. the specific use of the situation also needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.