关键词:C++
在 C++ 的实际代码编写中,结构体(类)的内存大小会进行对齐。
内存对齐的测试代码
定义若干个结构体,测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream>
using namespace std;
struct DataX { int a; int b; int c; int d; };
#define INFOSTRUCT(STRUCT) \ cout << #STRUCT << "\t" << sizeof(STRUCT) << "\t" << offsetof(STRUCT, a) << "\t" << offsetof(STRUCT, b) << "\t" << offsetof(STRUCT, c) << "\t" << offsetof(STRUCT, d) << endl
int main() { INFOSTRUCT(DataX); return 0; }
|
offsetof
是一个宏,可以计算成员的偏移量。
理解例子
Data0 & Data1 & Data2
先来看这三个结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct Data0 { char a; char b; short c; int d; };
struct Data1 { short a; char b; int c; char d; };
struct Data2 { char a; short b; char c; int d; };
|
代码在 VS 中输出是:
1 2 3
| Data0 8 0 1 2 4 Data1 12 0 2 4 8 Data2 12 0 2 4 8
|
在 Ubuntu-18.04 Clang++ 编译后是:
1 2 3 4 5
| fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test fingsinz@FingsinzStudio:~/playground$ ./test Data0 8 0 1 2 4 Data1 12 0 2 4 8 Data2 12 0 2 4 8
|
这三个结构体的成员变量都有两个 char
、一个 short
和一个 int
,因为声明位置不同,导致了内存大小分别为8、12、12。
根据已知的内存大小和成员偏移量,尝试表示出其内存布局:
Data3 & Data4
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct Data3 { int a; double b; char c; short d; };
struct Data4 { int a; double b; char c; short d; int e; };
|
代码在 VS 中输出是:
1 2
| Data3 24 0 8 16 18 Data4 24 0 8 16 18
|
在 Ubuntu-18.04 Clang++ 编译后是:
1 2 3 4
| fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test fingsinz@FingsinzStudio:~/playground$ ./test Data3 24 0 8 16 18 Data4 24 0 8 16 18
|
这两个结构体, Data4
是在 Data3
的基础上增加了一些新的成员变量形成的,但是内存大小都为 24。
根据已知的内存大小和成员偏移量,尝试表示出其内存布局:
Data3 |
Data4 |
 |
 |
- 在内存对齐下,
Data3
的最后几个字节浪费了,而 Data4
增加的新成员正好利用上了,而且还不会额外增加内存大小。
- 内存对齐为 8 字节,内存大小为 24 字节。
Data5 & Data6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #pragma pack(2) struct Data5 { short a; char b; int c; char d; }; #pragma pack()
struct alignas(8) Data6 { short a; char b; int c; char d; };
|
代码在 VS 中输出是:
1 2
| Data5 10 0 2 4 8 Data6 16 0 2 4 8
|
在 Ubuntu-18.04 Clang++ 编译后是:
1 2 3 4
| fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test fingsinz@FingsinzStudio:~/playground$ ./test Data5 10 0 2 4 8 Data6 16 0 2 4 8
|
结构体 Data5
使用了 #pragma pack()
的方式规定了内存对齐的大小;而结构体 Data6
使用 C++11 的关键字 alignas()
规定内存对齐的大小。
根据已知的内存大小和成员偏移量,尝试表示出其内存布局:
Data5 |
Data6 |
 |
 |
Data7 & Data8 & Data9 & Data10
对于复杂结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| struct Data7 { char a; struct _Data { char _a[5]; } b; int c; short d; };
struct Data8 { char a; struct _Data { int _a; char _b; } b; int c; short d; };
struct Data9 { char a; struct _Data { char _a; int _b; } b; int c; short d; };
struct Data10 { char a; struct alignas(8) _Data { char _a; int _b; } b; int c; short d; };
|
代码在 VS 中输出是:
1 2 3 4
| Data7 16 0 1 8 12 Data8 20 0 4 12 16 Data9 20 0 4 12 16 Data10 24 0 8 16 20
|
在 Ubuntu-18.04 Clang++ 编译后是:
1 2 3 4 5 6
| fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test fingsinz@FingsinzStudio:~/playground$ ./test Data7 16 0 1 8 12 Data8 20 0 4 12 16 Data9 20 0 4 12 16 Data10 24 0 8 16 20
|
尝试画出内存布局图:
-
Data7
:
- 内嵌结构体内存对齐为 1 字节,内存大小为 5 字节。
- 外部结构体内存对齐为 4 字节,内存大小为 16 字节。
-
Data8
:
- 内嵌结构体内存对齐为 4 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 4 字节,内存大小为 20 字节。
-
Data9
:
- 内嵌结构体内存对齐为 4 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 4 字节, 内存大小为 20 字节。
-
Data10
:
- 内嵌结构体内存对齐为 8 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 8 字节, 内存大小为 24 字节。
Data11 & Data12 & Data13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| struct Data11 { char a; struct _Data { double _a; } b; int c; short d; };
struct Data12 { char a; struct _Data { char _a[8]; } b; int c; short d; };
struct Data13 { char a; struct _Data { int _a[2]; } b; int c; short d; };
|
代码在 VS 中输出是:
1 2 3
| Data11 24 0 8 16 20 Data12 20 0 1 12 16 Data13 20 0 4 12 16
|
在 Ubuntu-18.04 Clang++ 编译后是:
1 2 3 4 5
| fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test fingsinz@FingsinzStudio:~/playground$ ./test Data11 24 0 8 16 20 Data12 20 0 1 12 16 Data13 20 0 4 12 16
|
尝试画出内存布局图:
-
Data11
:
- 内嵌结构体内存对齐为 8 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 8 字节,内存大小为 24 字节。
-
Data12
:
- 内嵌结构体成员为
char
数组,比较特殊,内存对齐为 1 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 4 字节,内存大小为 20 字节。
-
Data13
:
- 内嵌结构体内存对齐为 4 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 4 字节, 内存大小为 20 字节。
总结
- 一般情况下,默认最小对齐单位为 2 字节, 对齐最宽的基本类型 ( 如内存最大成员为
int
,则内存对齐为 4 )。 结构体的总大小为结构体最宽基本类型成员大小的整数倍。
- 嵌套成员的情况下( 如结构体成员包含结构体,结构体成员包含类等情况 ), 内部结构体对齐后视作整体再参与外部对齐,内存对齐等于成员的最大内存对齐。
char
类型比较特殊,趋向紧密。