关键词:C++
在 C++ 的实际代码编写中,结构体(类)的内存大小会进行对齐。
内存对齐的测试代码
定义若干个结构体,测试代码:
1 |
|
offsetof
是一个宏,可以计算成员的偏移量。
理解例子
Data0 & Data1 & Data2
先来看这三个结构体:
1 | struct Data0 { |
代码在 VS 中输出是:
1 | Data0 8 0 1 2 4 |
在 Ubuntu-18.04 Clang++ 编译后是:
1 | fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test |
这三个结构体的成员变量都有两个 char
、一个 short
和一个 int
,因为声明位置不同,导致了内存大小分别为8、12、12。
根据已知的内存大小和成员偏移量,尝试表示出其内存布局:
Data0 | Data1 | Data2 |
![]() |
![]() |
![]() |
-
结构体
Data0
的内存大小最小,排布最紧凑,空间利用最合理。- 内存对齐为 4 字节,内存大小为 8 字节。
-
结构体
Data1
和Data2
的内存布局中因为对齐机制,所以存在空的补位。- 内存对齐为 4 字节,内存大小为 12 字节。
Data3 & Data4
1 | struct Data3 { |
代码在 VS 中输出是:
1 | Data3 24 0 8 16 18 |
在 Ubuntu-18.04 Clang++ 编译后是:
1 | fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test |
这两个结构体, Data4
是在 Data3
的基础上增加了一些新的成员变量形成的,但是内存大小都为 24。
根据已知的内存大小和成员偏移量,尝试表示出其内存布局:
Data3 | Data4 |
![]() |
![]() |
- 在内存对齐下,
Data3
的最后几个字节浪费了,而Data4
增加的新成员正好利用上了,而且还不会额外增加内存大小。 - 内存对齐为 8 字节,内存大小为 24 字节。
Data5 & Data6
1 |
|
代码在 VS 中输出是:
1 | Data5 10 0 2 4 8 |
在 Ubuntu-18.04 Clang++ 编译后是:
1 | fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test |
结构体 Data5
使用了 #pragma pack()
的方式规定了内存对齐的大小;而结构体 Data6
使用 C++11 的关键字 alignas()
规定内存对齐的大小。
根据已知的内存大小和成员偏移量,尝试表示出其内存布局:
Data5 | Data6 |
![]() |
![]() |
-
Data5
规定了内存对齐为 2,所以在 10 个字节的时候就停下。如果把两个char
连在一起声明,会更省空间,达到大小为 8。- 内存对齐为 2 字节,内存大小为 10 字节。
-
Data6
规定了内存对齐为 8,所以比内存对齐为 4 的布局多出更多空间。- 内存对齐为 8 字节,内存大小为 16 字节。
Data7 & Data8 & Data9 & Data10
对于复杂结构体:
1 | struct Data7 { |
代码在 VS 中输出是:
1 | Data7 16 0 1 8 12 |
在 Ubuntu-18.04 Clang++ 编译后是:
1 | fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test |
尝试画出内存布局图:
Data7 | Data8 | Data9 | Data10 |
![]() |
![]() |
![]() |
![]() |
-
Data7
:- 内嵌结构体内存对齐为 1 字节,内存大小为 5 字节。
- 外部结构体内存对齐为 4 字节,内存大小为 16 字节。
-
Data8
:- 内嵌结构体内存对齐为 4 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 4 字节,内存大小为 20 字节。
-
Data9
:- 内嵌结构体内存对齐为 4 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 4 字节, 内存大小为 20 字节。
-
Data10
:- 内嵌结构体内存对齐为 8 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 8 字节, 内存大小为 24 字节。
Data11 & Data12 & Data13
1 | struct Data11 { |
代码在 VS 中输出是:
1 | Data11 24 0 8 16 20 |
在 Ubuntu-18.04 Clang++ 编译后是:
1 | fingsinz@FingsinzStudio:~/playground$ clang++ test.cpp -o test |
尝试画出内存布局图:
Data11 | Data12 | Data13 |
![]() |
![]() |
![]() |
-
Data11
:- 内嵌结构体内存对齐为 8 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 8 字节,内存大小为 24 字节。
-
Data12
:- 内嵌结构体成员为
char
数组,比较特殊,内存对齐为 1 字节,内存大小为 8 字节。 - 外部结构体内存对齐为 4 字节,内存大小为 20 字节。
- 内嵌结构体成员为
-
Data13
:- 内嵌结构体内存对齐为 4 字节,内存大小为 8 字节。
- 外部结构体内存对齐为 4 字节, 内存大小为 20 字节。
总结
- 一般情况下,默认最小对齐单位为 2 字节, 对齐最宽的基本类型 ( 如内存最大成员为
int
,则内存对齐为 4 )。 结构体的总大小为结构体最宽基本类型成员大小的整数倍。 - 嵌套成员的情况下( 如结构体成员包含结构体,结构体成员包含类等情况 ), 内部结构体对齐后视作整体再参与外部对齐,内存对齐等于成员的最大内存对齐。
char
类型比较特殊,趋向紧密。