当前位置:嗨网首页>书籍在线阅读

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 ,不要请求其对齐值是 12 。该说明符用作声明的一部分,说明符后面的圆括号内包含对齐值或类型:

_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 头文件后,就可以把 alignasalignof 分别作为 _Alignas_Alignof 的别名。这样做可以与C++关键字匹配。

C11在 stdlib.h 库还添加了一个新的内存分配函数,用于对齐动态分配的内存。该函数的原型如下:

void *aligned_alloc(size_t alignment, size_t size);

第1个参数代表指定的对齐,第2个参数是所需的字节数,其值应是第1个参数的倍数。与其他内存分配函数一样,要使用 free() 函数释放之前分配的内存。