8. _AlignAs 关键字
_AlignAs 关键字 是 C11 标准引入的关键字,它的作用是显式指定一个变量或结构体成员变量在内存中的对齐方式。
_AlignAs 关键字 可以强制一个变量、数组、或结构体以及结构体成员的起始地址以某个数值(通常是 2 的 x 次方的值,如:1, 2, 4, 8)的整数倍进行对齐。这种对齐通常是为了满足某种硬件需求或提高 CPU 的运行速度。
前面我们学过 #pragma pack(n) 这个预处理指令,这个预处理指令会在预处理阶段来压缩一个结构体内部的成员变量,使其按给定的对齐字节数进行排列,但这个指令只压缩结构体的对齐字节数,并不会扩大对齐字节数。
_AlignAs 关键字 和 #pragma pack(n) 预处理指令不同,它是编译阶段的关键字。它不会压缩结构体成员的字节对齐数,它只会扩大某个变量字节对齐数(但不会缩小)。如默认 double 类型的变量 d 在 64 位系统中的起始地址一定是 8 的整数倍。我们通过 _AlignAs 关键字 可以让其变为 16 的整数倍、32 的整数倍,但不能让其起始地址变为 4 的整数倍或 2 的整数倍。
语法:
// 根据 数据类型名 的对齐数作为最小对齐字节数
_Alignas(数据类型名)
// 给定 对齐字节数
_Alignas(整数常量表达式)
说明
_Alignas对齐属性不得用于声明说明,如:typedef、位域、函数、形式参数、以及使用 register 声明的变量或对象。- 整数常量表达式 的计算值的应为有效的对齐字节数(即 2 的 x 次方的值,如:1, 2, 4, 8等)。
- 整数常量表达式 的计算值不得小于修饰变量的最小对齐字节数。
_Alignas(数据类型名)等同于_Alignas(_Alignof(数据类型名))。即根据数据类型的来计算对齐字节数。- 如果整数常量表达式的值为 0,即
_Alignas(0),则此_Alignas关键字则不再有效(等同于没有对齐要求)。
示例:
// filename: alignas.c
#include <stdio.h>
struct mystruct {
char c;
_Alignas(4) short int s;
int i;
} t;
int main(int argc, char * argv[]) {
char c; // 默认起始地址 1 字节对齐。
short s; // 默认起始地址 2 字节对齐。
int i; // 默认起始地址 4 字节对齐。
double d; // 默认起始地址 8 字节对齐。
_Alignas(16) double a16d; // 起始地址修改为 16 的整数倍。
_Alignas(256) char a256c; // 起始地址修改为 256 的整数倍。
printf("&c: %p\n", &c);
printf("&s: %p\n", &s);
printf("&i: %p\n", &i);
printf("&d: %p\n", &d);
printf("&a16d: %p\n", &a16d); // 地址的后 1 位一定是0
printf("&a256c: %p\n", &a256c); // 地址的后 2 位一定是00
printf("sizeof(t): %ld\n", sizeof(t));
printf("_Alignof(t): %ld\n", _Alignof(t));
printf("&t: %p\n", &t);
printf("&t.c: %p\n", &t.c);
printf("&t.s: %p\n", &t.s);
printf("&t.i: %p\n", &t.i);
return 0;
}
编译和运行结果如下:
weimingze@mzstudio:~$ gcc -o alignas alignas.c
weimingze@mzstudio:~$ ./alignas
&c: 0x7ffc86e71cd9
&s: 0x7ffc86e71cda
&i: 0x7ffc86e71cdc
&d: 0x7ffc86e71cf0
&a16d: 0x7ffc86e71ce0
&a256c: 0x7ffc86e71c00
sizeof(t): 12
_Alignof(t): 4
&t: 0x60f5972f4018
&t.c: 0x60f5972f4018
&t.s: 0x60f5972f401c
&t.i: 0x60f5972f4020
从上述运行结果可知, 变量 a16d 的地址是 0x7ffc86e71ce0 是 16 的整数倍。变量 a256c 的地址是 0x7ffc86e71c00 是 256 的整数倍。
上述定义的结构体
struct mystruct {
char c;
_Alignas(4) short int s;
int i;
} t;
则 t 的内存结构是 如下 12 字节的内存结构。
空 空 空 空 空
| | | | |
V V V V V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| t.c | | | | t.s | | | t.i |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
^ ^ ^
| | |
&t.c &t.s &t.i
&t
如果 strcut mystruct 的声明改写如下
struct mystruct {
char c;
_Alignas(0) short int s; // 等同于 short int s;
int i;
} t;
则 t 的内存结构会变为 如下的 8 个字节的内存结构。
空
|
V
+-----+-----+-----+-----+-----+-----+-----+-----+
| t.c | | t.s | t.i |
+-----+-----+-----+-----+-----+-----+-----+-----+
^ ^ ^
| | |
&t.c &t.s &t.i
&t
实验
写程序,声明如下结构体变量的 ss, 通过打印地址的方式分析 ss 结构体在内存中的结构和占用字节数。
struct mystruct {
char c;
_Alignas(256) short int s;
int i;
} ss;