22-对齐特性(C11)
15.5 对齐特性(C11)
C11的对齐特性比用位填充字节更自然,它们还代表了C在处理硬件相关问题上的能力。在这种上下文中,对齐指的是如何安排对象在内存中的位置。例如,为了效率最大化,系统可能要把一个 double 类型的值存储在4字节内存地址上,但却允许把 char 存储在任意地址。大部分程序员都对对齐不以为然。但是,有些情况又受益于对齐控制。例如,把数据从一个硬件位置转移到另一个位置,或者调用指令同时操作多个数据项。
_Alignof 运算符给出一个类型的对齐要求,在关键字 _Alignof 后面的圆括号中写上类型名即可:
size_t d_align = _Alignof(float);
假设 d_align 的值是 4 ,意思是 float 类型对象的对齐要求是 4 。也就是说, 4 是存储该类型值相邻地址的字节数。一般而言,对齐值都应该是 2 的非负整数次幂。较大的对齐值被称为stricter或stronger,较小的对齐值被称为weaker。
可以使用 _Alignas 说明符指定一个变量或类型的对齐值。但是,不应该要求该值小于基本对齐值。例如,如果 float 类型的对齐要求是 4 ,不要请求其对齐值是 1 或 2 。该说明符用作声明的一部分,说明符后面的圆括号内包含对齐值或类型:
_Alignas(double) char c1;
_Alignas(8) char c2;
unsigned char _Alignas(long double) c_arr[sizeof(long double)];
注意 撰写本书时,Clang(3.2版本)要求
_Alignas(type)说明符在类型说明符后面,如上面第3行代码所示。但是,无论_Alignas(type)说明符在类型说明符的前面还是后面,GCC 4.7.3都能识别,后来Clang 3.3 版本也支持了这两种顺序。
程序清单15.5中的程序演示了 _Alignas 和 _Alignof 的用法。
程序清单15.5 align.c 程序
// align.c -- 使用 _Alignof 和 _Alignas (C11)
#include <stdio.h>
int main(void)
{
double dx;
char ca;
char cx;
double dz;
char cb;
char _Alignas(double) cz;
printf("char alignment: %zd\n", _Alignof(char));
printf("double alignment: %zd\n", _Alignof(double));
printf("&dx: %p\n", &dx);
printf("&ca: %p\n", &ca);
printf("&cx: %p\n", &cx);
printf("&dz: %p\n", &dz);
printf("&cb: %p\n", &cb);
printf("&cz: %p\n", &cz);
return 0;
}
该程序的输出如下:
char alignment: 1
double alignment: 8
&dx: 0x7fff5fbff660
&ca: 0x7fff5fbff65f
&cx: 0x7fff5fbff65e
&dz: 0x7fff5fbff650
&cb: 0x7fff5fbff64f
&cz: 0x7fff5fbff648
在我们的系统中, double 的对齐值是 8 ,这意味着地址的类型对齐可以被8整除。以0或8结尾的十六进制地址可被8整除。这就是地址常用两个 double 类型的变量和 char 类型的变量 cz (该变量是 double 对齐值)。因为 char 的对齐值是 1 ,对于普通的 char 类型变量,编译器可以使用任何地址。
在程序中包含 stdalign.h 头文件后,就可以把 alignas 和 alignof 分别作为 _Alignas 和 _Alignof 的别名。这样做可以与C++关键字匹配。
C11在 stdlib.h 库还添加了一个新的内存分配函数,用于对齐动态分配的内存。该函数的原型如下:
void *aligned_alloc(size_t alignment, size_t size);
第1个参数代表指定的对齐,第2个参数是所需的字节数,其值应是第1个参数的倍数。与其他内存分配函数一样,要使用 free() 函数释放之前分配的内存。