Why memory alignment

1. Platform reasons (porting reasons) : Not all hardware platforms can access any data at any address; Some hardware platforms can only fetch certain types of data at certain addresses or throw hardware exceptions.

2. Performance reasons: Data structures (especially stacks) should be aligned on natural boundaries as much as possible. The reason is that in order to access unaligned memory, the processor needs to make two memory accesses; Aligned memory access requires only one access.

Memory alignment rule

1. The offset of the first member of the structure is 0, and the offset of each subsequent member relative to the first address of the structure is an integer multiple of the smaller of the size and valid alignment value (assuming n) of the member. This is simply an integer multiple of min(n, the size of the member).

2. The size of the structure is an integer multiple of the valid alignment value (let’s say N), and the compiler populates bytes after the last member if necessary. It’s just an integer multiple of n.

3. If a structure has some structure members, the structure members are stored from the address of the internal offset of the maximum element size.

Valid alignment value: 1. When not explicitly specified, the length of the longest member in the structure is valid. 2, Use #pragma pack(n) to specify the alignment factor n, which is the smaller of n and the length of the longest member in the structure. 3. Compilers generally set the default alignment coefficient.

For 🌰 analysis

The default alignment coefficient of the compiler in the test environment is 8

In order toStruct T1As an example

code
struct T1 { double a; char b; int c; short d; } t1; size_t s = typeof(t1); printf("%zu", s); // Prints 24Copy the code
Member memory distribution

Analysis of the

1. No memory alignment coefficient is specified. The current compiler defaults to 8. 2. Double A occupies 8 bytes and is stored from offset 0 with memory distribution 0x00 to 0x07. 3, char B occupies 1 byte, stored from offset 8, memory distribution 0x08. 4, int c occupies 4 bytes, offset = min(8, 4) = multiple of 4, offset = offset (9) = multiple of 4, offset = offset (12) C is stored at offset 12 and the memory distribution is 0x0C to 0x10. 5. Short D occupies 2 bytes and is stored from offset 16 with memory distribution 0x11 to 0x12. 6. Refer to rule 2, size = multiple of 8. Although T1 occupies 18 bytes in memory distribution, it does not meet the rule. The smallest common multiple of 8 greater than 18 is 24, so the size of T1 is 24 bytes.

usexcodetheView Memoryvalidation
typedef struct T {
    double a;
    char b;
    int c;
    short d;
}T, * P_T;
Copy the code

willT1Change forT2

code
struct T2 { double a; int b; char c; short d; } t2; size_t s = typeof(t2); printf("%zu", s); // Prints 16Copy the code
Member memory distribution

Analysis of the

1. No memory alignment coefficient is specified. The current compiler defaults to 8. 2. Double A occupies 8 bytes and is stored from offset 0 with memory distribution 0x00 to 0x07. 3, int B occupies 4 bytes, stored from offset 8, 0x08 to 0x0b 4, char C occupies 1 byte, stored from offset 12, memory distribution 0x12 Short d occupies 2 bytes. According to memory alignment rule 1, offset = min(8, 2) = multiple of 2. If offset 13 is not satisfied, continue to address backward until offset 14. Short D is stored at offset 14 and the memory distribution is 0x0E to 0x10. 6. T2 occupies 16 bytes in memory distribution, which satisfies rule 2. T2 is 16 bytes.

struct T2withstruct T1Basically similar, no more verification

Conclusion: Compared with T1 and T2, in the case of the same structure, the size of memory occupied by the structure is different due to the existence of memory alignment. Therefore, the influence of memory alignment on the size of the structure should be considered when designing the structure.

The structure is the member

typedef struct inner { short in_a; int in_b; }Inner, * P_Inner; typedef struct outter{ char a; Inner b; char c; long d; char e; }Outter, * P_Outter; size_t s = typeof(Outter); printf("%zu", s); // Prints 32Copy the code
Member memory distribution

Analysis of the

1. No memory alignment coefficient is specified. The current compiler defaults to 8. 2, char A occupies 1 byte, stored from offset 0, memory distribution 0x00. 3, Inner B occupies 8 bytes, offset = Max (Inner member size) = multiple of 4, offset 1 is not correct, continue to address until offset 4, then Inner B is stored from offset 4. Memory distribution 0x04 to 0x0B. Short in_A and int IN_B refer to struct T1 above for analysis. 5, char C occupies byte 1, stored from offset 12, 0x0c. Offset = Max (8, 8) = Max (8, 8) = Max (8, 8) = Max (8, 8) = Max (8, 8) = Max (8, 8) = Max (8, 8) = Max (8, 8) 7, char e occupies byte 1, stored from offset 24, memory distribution 0x19 8. Refer to rule 2, size = multiple of 8. Although the Outter occupies 25 bytes in the memory distribution, it does not meet the rule. The smallest multiple of 8 greater than 25 is 32, so the size of the Outter is 32 bytes.

View Memoryvalidation

Set the memory alignment coefficient

Use precompiled instructions#pragma pack(n)To set the alignment coefficient, use#pragma pack()Cancels the set alignment coefficient

No alignment coefficient is set. The current compiler defaults to 8
typedef struct p { char a; int b; char c; long d; }P; printf("%zu \n", sizeof(P)); // Print the result: 24Copy the code
Set the alignment coefficient to1
#pragma pack(1) typedef struct p1 { char a; int b; char c; long d; }P1; #pragma pack() printf("%zu \n", sizeof(P1)); // Print the result: 14Copy the code
Set the alignment coefficient to2
#pragma pack(2) typedef struct p2 { char a; int b; char c; long d; }P2; #pragma pack() printf("%zu \n", sizeof(P2)); // Print the result: 16Copy the code
Set the alignment coefficient to4
#pragma pack(4) typedef struct p3 { char a; int b; char c; long d; }P3; #pragma pack() printf("%zu \n", sizeof(P3)); // Print the result: 20Copy the code
Set the alignment coefficient to8
#pragma pack(8) typedef struct p4 { char a; int b; char c; long d; }P4; #pragma pack() printf("%zu \n", sizeof(P4)); // Print the result: 24Copy the code

1Byte alignment, or compiler instructions can be used__attribute__((packed))

typedef struct p5 { char a; int b; char c; long d; } __attribute__((packed)) P5; print("%zu \n", sizeof(P5)); // Print the result: 14Copy the code

The above structures do not specifically analyze the memory structure, refer to the above analysis.

__attribute __((aligned(n)))

This property specifies the minimum alignment of variables of the specified type (assuming n, where n must be greater than 0 to the power of 2), to the maximum length of any member in the structure greater than n.

n> Maximum member length

typedef struct T { long a; char b; int c; char d; } __attribute__((aligned(16))) T, *P_T; printf("%zu \n", sizeof(T)); // Print the result: 32Copy the code

The valid alignment value is 16

n> maximum member length and use#pragma pack(n)Specify alignment coefficient

#pragma pack(4) typedef struct T { long a; char b; int c; char d; } __attribute__((aligned(16))) T, *P_T; #pragma pack() printf("%zu \n", sizeof(T)); // Print the result: 32Copy the code

#pragma pack(4) is not in effect with a valid alignment value of 16

n< The length of a member

typedef struct T { long a; char b; int c; char d; } __attribute__((aligned(4))) T, *P_T; printf("%zu \n", sizeof(T)); // Print the result: 24Copy the code

The valid alignment value is the maximum member long A of length 8

n< a member length, and use#pragma pack(n)Specify alignment coefficient

#pragma pack(4) typedef struct T { long a; char b; int c; char d; } __attribute__((aligned(4))) T, *P_T; #pragma pack() printf("%zu \n", sizeof(T)); // Print the result: 20Copy the code

#pragma pack(4) takes effect with an effective alignment value of 4

Added: Optimization of Xcode compile-time objects

For example, 🌰, in main.m we write the following class

@interface MyObject: NSObject

@property (nonatomic, assign) int i1;
@property (nonatomic, assign) double d;
@property (nonatomic, assign) int i2;

@end

@implementation MyObject

@end
Copy the code

Enter the command xcrun-sdk iphoneos clang-arch arm64-rewrite-objc main.m -o main-arm64. CPP to get the c++ file. Search MyObject_IMPL in the file to find the following structure

struct MyObject_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	int _i1;
	int _i2;
	double _d;
};
Copy the code

Here you can see that the MyObject class converted to the MyObject_IMPL structure changes the structure of the member variables. If you were to convert MyObject directly, it would look like this:

struct MyObject_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	int _i1;
	double _d;
	int _i2;
};
Copy the code

The current compiler default memory alignment factor is 8, so the sizeof this structure is 32. The sizeof structure in the main-arm64.cpp file is 24. As you can see, Xcode optimizes the structure members when converting a class to a structure.