Reproduced in: https://www.cnblogs.com/ye-moooooo/p/4601189.html related to __align (n)
The __align keyword instructs the compiler to align variables on n-byte boundaries.
__align is a storage class modifier. It does not affect the type of the function. Grammar
void foo (void)
{
...
__align (+) int i; This alignment value was not permitted for
//a local variable ...
}
__align (+) int i; Permitted as a global variable.
--------------------------------------------------------------------------------------------------------------- --------------------------------------
--------------------------------------------------------------------------------------------------------------- --------------------------------------
I. What is byte alignment and why is it aligned?
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.
Two. Byte alignment affects the program:
Let's take a look at a few examples (32bit,x86 environment, GCC compiler):
Set the struct as defined below:
struct A
{
int A;
Char b;
short C;
};
struct b
{
char b;
int A;
short C;
}; The
now knows the lengths of the various data types on the 32-bit machine as follows:
Char:1 (signed unsigned same)
Short:2 (Signed and unsigned)
Int:4 (Signed unsigned same)
Long:4 (Signed and unsigned)
float:4 Double:8
So what's the size of the top two structures?
The result:
sizeof (Strcut A) value is 8
sizeof (struct B) is a value of
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 ().
Three. What are the principles for the compiler to align?
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). This makes it impossible to understand the values of several examples above.
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 with 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.
Four. How do I modify the compiler's default alignment values?
1. In the VC IDE, you can modify this: [project]| [settings],c/c++ tab category of the Code generation option of the struct Member alignment modified, the default is 8 bytes.
2. When encoding, you can change this dynamically: #pragma pack. Note: It is pragma instead of progma.
Five. What do we consider in programming for byte alignment?
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.
Six. Possible pitfalls of 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.
Seven. 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.
Eight. Snap-to-handle under arm
From Dui0067d_ads1_2_complib
3.13 Type Qulifiers
Partial excerpt from ARM compiler document alignment Section
Alignment use:
1.__align (num)
This is used to modify the byte bounds of the highest-level object.
uses this command __align (8) to make a cosmetic restriction when using LDRD or STRD in the assembly. To ensure that the data Objects are aligned accordingly. The
command for this decorated object is limited to 8 bytes, allowing 2-byte objects to be 4 bytes
aligned, but not 4 bytes of object 2 bytes.
__align is a storage class modification, and he only modifies the top-level type objects that cannot be used for structs or function objects.
2.__packed
__packed is a byte-aligned
1. Cannot align packed objects
2. Read-write access for all objects is non-aligned access
3.float and the structure with float and unused __packed objects will not be byte-aligned
4.__packed has no effect on local shaping variables
5. Forcing conversions from unpacked objects to packed objects is undefined, The shaping pointer can be legally set to
meaning packed.
__packed int* p;//__packed int is meaningless
6. Alignment or non-aligned read and write access poses a problem
__packed struct struct_test
{
char A;
int b;
Char C;
}; //define the following structure at this point the start address of B must be misaligned
// There may be a problem accessing B in the stack because the data on the stack is definitely aligned to access [from CL]
//To define the following variables as global static not on the stack
static char* p;
static struct struct_test A;
void Main ()
{
__packed int* Q;//is now defined as __packed to decorate the current Q point to a non-aligned data address below the access is possible
p = (char*) &a;
Q = (int*) (p+1);
*q = 0x87654321;
/*
The assembly instructions that get the assignment are clear.
LDR r5,0x20001590; = #0x12345678
[0xe1a00005] mov r0,r5
[0xeb0000b0] BL __rt_uwrite4//Call a write 4byte operation function here
[0xe5c10000] Strb r0,[r1, #0]//function to perform 4 STRB operations and then return to ensure the correct access to the data
[0xe1a02420] mov r2,r0,lsr #8
[0xe5c12001] Strb r2,[r1, #1]
[0xe1a02820] mov r2,r0,lsr #16
[0xe5c12002] Strb r2,[r1, #2]
[0XE1A02C20] mov r2,r0,lsr #24
[0xe5c12003] Strb r2,[r1, #3]
[0xe1a0f00e] mov pc,r14
*/
/*
If Q does not have a __packed modifier then the assembler command is such that direct access to the singular address fails
[0xe59f2018] Ldr r2,0x20001594; = #0x87654321
[0xe5812000] str r2,[r1, #0]
*/
This makes it clear how non-aligned access produces errors.
And how to eliminate non-aligned access poses problems
You can also see non-aligned Access and aligned access instruction differences leading to efficiency issues
}
Nine. Consequences of misaligned access to borders under arm
From http://blog.csdn.net/alenwelkin/archive/2006/12/19/1448324.aspx
Wrote a program by itself, defining a char array as follows:
Char p[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
Using long and short pointers to the location of the p+3, this address is 0x10963 after compilation, obviously an address that is not aligned to long and short.
The output of the program is as follows, the contents in parentheses, and the address in front.
/mnt $./test
L 0x10963 (0x30201040), S 0x10963 (0x5040), Lt 0x30201040, St 0x5040
Using the Arm-linux-objdump tool to disassemble the executable file, the compiler has made special handling of the short pointer reference to ensure its reference is correct, and the long pointer did not.
unsigned long *l = (p + 3);
83e0:e59f3084 LDR R3, [PC, #132]; 846c <.text+0x190>
83e4:e50b3010 STR R3, [FP, #-16]
unsigned short *s = (p + 3);
83E8:E59F307C LDR R3, [PC, #124]; 846c <.text+0x190>
83ec:e50b3014 STR R3, [FP, #-20]
unsigned long lt = *l;
83f0:e51b3010 LDR R3, [FP, #-16]//variable L->R3
83f4:e5933000 LDR R3, [R3]//l content->R3
83f8:e50b3018 STR R3, [FP, #-24]//r3->lt
unsigned short st = *s;
83fc:e51b3014 LDR R3, [FP, #-20]//variable S->R3
8400:e5d32000 LDRB R2, [R3]//s content (low)->r2
8404:e5d33001 LDRB R3, [R3, #1]//s content (high)->R3
8408:e1823403 Orr R3, R2, R3, LSL #8//R3 after left shift with R2 spell short value->R3
840C:E54B301A STRB R3, [FP, #-26]//R3 short value (low), variable St low
8410:E1A03443 mov r3, R3, ASR #8//R3 right shift 8-bit->R3
8414:e54b3019 STRB R3, [FP, #-25]//R3 short value (high), variable St high
How the 0x30201040 came. Thought for a long while do not understand.
Refer to Arm related books found that if both 1-bit and 22-bit in coprocessor CP15:c1:c0 are 0, the return value of the arm instruction Ldr is memory (addr & To, 4) ROR ((addr & 3) * 8). The meaning of the first half of the sentence is to take the 4 boundary down, in this case is 0x10960, then take its content is 0x40302010, the second half of the sentence in this example is the loop right 24 bits. Together See is 0x40302010 ROR = 0x30201040, and this example coincides.