Pie*_*rre 0 c++ memory-alignment
根据这个答案:
最小对齐(在给定平台上)是不会崩溃的对齐。
在GCC 8中,有两个函数可以获取最小对齐和首选对齐:
alignof给出最小对齐的标准运算符__alignof__给出首选对齐方式的GNU函数对于 a double,最小对齐为 4 字节,在 i386 架构上首选对齐为 8 字节。因此,如果我正确理解了上面引用的答案,即应用程序将 a 存储double在不是 4 的倍数的地址处,则程序应该崩溃。
我们看下面的代码:
#include <iostream>
void f(void* ptr) {
double* ptr_double = (double*) ptr;
ptr_double[0] = 3.5;
std::cout << ptr_double[0] << std::endl;
std::cout << &ptr_double[0] << std::endl;
}
int main()
{
alignas(__alignof__(double)) char arr[9];
f(arr+1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我使用-m32选项编译它,它运行良好并且得到以下结果:
3.5
0xffe41571
Run Code Online (Sandbox Code Playgroud)
我们可以看到我的double没有对齐,但程序运行没有任何问题。
上面引用的下一句话是:
在 x86-64 上它是一个字节。
在某些方面,这似乎是正确的,因为我的代码可以工作。但是,在这种情况下,为什么alignof返回 4?
问题出在哪里,就在这里?最小对齐的给定定义是否错误?还是有什么我不明白的地方?
最小对齐(在给定平台上)是不会崩溃的对齐。
因此,如果我正确理解了上面引用的答案,即在不是 4 的倍数的地址存储双精度值的应用程序,程序应该崩溃。
你在否认前因。
仅仅因为符合对齐不会导致崩溃,并不意味着未对齐会导致崩溃。
C++ 标准是这样说的:
[expr.alignof]alignof 表达式产生其操作数类型的对齐要求。
[basic.align] 对象类型具有对齐要求([basic.fundamental]、[basic.compound]),这些要求对该类型的对象可以分配的地址施加限制。对齐是实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数。对象类型对该类型的每个对象施加对齐要求;可以使用对齐说明符来请求更严格的对齐。
就 C++ 语言而言,不存在未对齐的对象,因此它没有指定有关其行为的任何信息。您正在做的是访问一个不存在的对象,并且程序的行为是未定义的。
某些 CPU 架构,特别是您正在使用的架构1,在使用未对齐的内存地址时不会崩溃。这样的操作或多或少会慢一些。
但是,在这种情况下,为什么alignof 返回4?
因为语言实现选择了这样。大概是因为它比使用 1 或 2 快,但不比使用 8 快。
1 80386 的程序员参考手册是这样说的:
请注意,字不需要在偶数地址处对齐,双字不需要在可被四整除的地址处对齐。这允许数据结构(例如,包含混合字节、字和双字项的记录)的最大灵活性和存储器利用的效率。当在 32 位总线配置中使用时,处理器和内存之间的实际数据传输以双字为单位,从可被 4 整除的地址开始;然而,处理器将对未对齐字或双字的请求转换为存储器接口可接受的适当请求序列。这种未对齐的数据传输需要额外的内存周期,从而降低了性能。为了获得最大性能,数据结构(包括堆栈)的设计方式应尽可能使字操作数在偶数地址上对齐,而双字操作数在可被四整除的地址上对齐。由于 CPU 内的指令预取和排队,不需要指令在字或双字边界上对齐。(但是,如果控制传输的目标地址可以被四整除,则速度会略有提高。)
然而,i386 的后续架构引入了向量扩展,这确实需要对齐。
结论:GCC 文档对于“最小对齐”的含义与 Starynkevitch 的定义不同。
| 归档时间: |
|
| 查看次数: |
149 次 |
| 最近记录: |