我正在尝试构建一个简单的链表,其中每个元素将与某个圆形地址对齐。 我尝试了以下代码(尽可能缩短为SSCCE):
#include "stdio.h"
#define N 10
typedef struct __attribute__((aligned(0x100)) _element {
int val;
char padding[64];
struct _element *next;
} element;
int main() {
element* head = new element;
element* current = head;
for (int i = 0; i < N; ++i) {
current->val = i;
if (i == N - 1)
break;
current->next = new element;
current = current->next;
}
current->next = NULL;
current = head;
printf("sizeof(element) = 0x%x\n", (unsigned int)sizeof(element));
while (current) {
printf("*(%p) = %d\n", ¤t->val, current->val);
current = current->next;
}
return 0;
}
使用g ++ 4.2.2构建,没有优化,并产生:
sizeof(element) = 0x100
*(0x501010) = 0
*(0x501120) = 1
*(0x501230) = 2
*(0x501340) = 3
*(0x501450) = 4
*(0x501560) = 5
*(0x501670) = 6
*(0x501780) = 7
*(0x501890) = 8
*(0x5019a0) = 9
为什么地址不对齐到0x100?请注意,它确实会影响结构“大小”,看起来它是以某种方式填充,但它不会像我想要的那样从对齐的地址开始。
从this answer我理解可能存在最大对齐,但即使将其降低到0x20也不会改变对齐(仅限sizeof)。 This answer没有帮助,因为它是关于堆栈分配的。找不到任何其他来源来解释这一点。我从这个属性中问了太多吗?或者做错了什么?
提前致谢!
答案 0 :(得分:4)
您正在使用gcc的C扩展来定义对齐方式。这将影响对象的大小(需要是对齐的倍数),可能影响具有静态对齐的对象的存储布局,但仅影响链接器准备的程度对齐这些对象。它不会影响动态分配对象的对齐,即使在C ++中也是如此。
C ++ new
运算符调用分配器函数来提供存储,然后通过调用对象构造函数来初始化存储。该标准要求分配函数返回一个“适当对齐的指针,以便它可以转换为具有基本对齐要求的任何完整对象类型的指针”。 (§3.7.4.1p2)。默认标准库全局分配器必须符合此要求(第18.6.1节)。 “基本对齐要求”的定义是特定于实现的,但它必须至少与任何标量类型的对齐要求一样大,并且不需要更大。
C ++标准规定的全局分配函数只有一个参数,即所请求对象的大小。它不会传递有关对象或对象类型的任何信息,因此它无法执行特定于对齐的分配。
您可以自由地为给定类型定义分配函数,该函数可以考虑该类型的对齐。但是,它必须使用非标准(即系统特定的)底层分配器,或者过度分配然后调整指针。 (C ++ 11为此提供了标准库函数align
。)
当我阅读标准时,如果对象类型的对齐超出了底层分配器的能力,C ++ 11要求new
表达式失败:
§3.11p9如果实现不支持特定上下文中特定扩展对齐的请求,则该程序格式错误。此外,对于无法遵守请求的对齐的动态存储的运行时分配请求应视为分配失败。
这不适用于你的程序,因为你使用的是GCC特定的面向C的对齐属性,当然不是C ++ 11,所以我认为编译器有权调用一个将返回的分配器存储空间不足,而不是抛出bad_alloc
。
顺便说一句,gcc 4.2.2刚刚庆祝了它的六岁生日。您应该考虑将其发送到小学并更新为更现代的东西。
答案 1 :(得分:1)
attribute(__aligned__())
非常可怕。我称之为虫子。在gcc列表中根本没有牵引力。甚至不承认任何人都读过它。
https://gcc.gnu.org/ml/gcc/2014-06/msg00308.html
请注意,您甚至无法检查所获得的内容是否已对齐,因为它会错误地计算出数学结果。
0xxxxxx30 % 0x40 == 0x0 //really? that's aligned to 64b?
posix_memalign()或胸围