88

我有以下代码:

#include <stdio.h>

int
main(void)
{
        float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

我有以下输出:

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac

为什么地址a[0]不是的倍数0x1000

究竟是__attribute__((aligned(x)))做什么的?我误解了这个解释?

我正在使用 gcc 4.1.2。

4

4 回答 4

99

我认为问题在于您的数组在堆栈上,并且您的编译器太旧而无法支持过度对齐的堆栈变量。GCC 4.6 及更高版本修复了该错误

C11/C++11alignas(64) float a[4];仅适用于任何 2 的幂对齐。
您使用的 GNU C__attribute__((aligned(x)))也是如此。

(在 C11 中,#include <stdalign.h>对于#define alignas _Alignas: cppref)。


但是在您的对齐非常大的情况下,到 4k 页面边界,您可能不希望它在堆栈上。

因为当函数启动时堆栈指针可以是任何东西,所以如果不分配比您需要的更多并对其进行调整,就无法对齐数组。(编译器将and rsp, -4096或等效且不使用分配的 0 到 4088 字节中的任何一个;根据该空间是否足够大进行分支是可能的,但由于巨大的对齐比数组或其他局部变量的大小大得多而无法完成不是正常情况。)

如果将数组移出函数并移入全局变量,它应该可以工作。您可以做的另一件事是将其保留为局部变量(这是一件非常好的事情),但将其static. 这将防止它被存储在堆栈中。请注意,这两种方式都不是线程安全或递归安全的,因为数组只有一个副本。

使用此代码:

#include <stdio.h>

float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

我明白了:

0x804c000 0x804c004 0x804c008 0x804c00c

这是预期的。使用您的原始代码,我只是像您一样获得随机值。

于 2009-05-08T19:41:05.947 回答
42

gcc 中有一个错误导致对齐的属性不适用于堆栈变量。它似乎已通过下面链接的补丁修复。下面的链接也包含对该问题的相当多的讨论。

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660

我已经用两个不同版本的 gcc 尝试了上面的代码:来自 RedHat 5.7 盒子的 4.1.2,它与您的问题类似地失败了(本地数组在 0x1000 字节边界上没有对齐)。然后我在 RedHat 6.3 上使用 gcc 4.4.6 尝试了您的代码,它完美地工作(本地数组已对齐)。Myth TV 的人也有类似的问题(上面的 gcc 补丁似乎已修复):

http://code.mythtv.org/trac/ticket/6535

无论如何,看起来您在 gcc 中发现了一个错误,该错误似乎在以后的版本中得到了修复。

于 2012-10-15T22:17:45.073 回答
13

最近的 GCC(用 4.5.2-8ubuntu4 测试)似乎可以按预期工作,阵列正确对齐。

#include <stdio.h>

int main(void)
{
    float a[4] = { 1.0, 2.0, 3.0, 4.0 };
    float b[4] __attribute__((aligned(0x1000))) = { 1.0, 2.0, 3.0, 4.0 };
    float c[4] __attribute__((aligned(0x10000))) = { 1.0, 2.0, 3.0, 4.0 };

    printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
    printf("%p %p %p %p\n", &b[0], &b[1], &b[2], &b[3]);
    printf("%p %p %p %p\n", &c[0], &c[1], &c[2], &c[3]);
}

我得到:

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c
于 2011-05-04T02:36:16.740 回答
9

对齐并非对所有类型都有效。您应该考虑使用结构来查看正在运行的属性:

#include <stdio.h>

struct my_float {
        float number;
}  __attribute__((aligned(0x1000)));

struct my_float a[4] = { {1.0}, {2.0}, {3.0}, {4.0} };

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

然后,您将阅读:

0x603000 0x604000 0x605000 0x606000

这是你所期待的。

编辑: 由@yzap 推动并遵循@Caleb Case 评论,最初的问题是由于 GCC version only造成的。我使用请求者的源代码检查了 GCC 3.4.6 与 GCC 4.4.1:

$ ./test_orig-3.4.6
0x7fffe217d200 0x7fffe217d204 0x7fffe217d208 0x7fffe217d20c
$ ./test_orig-4.4.1
0x7fff81db9000 0x7fff81db9004 0x7fff81db9008 0x7fff81db900c

现在很明显,较旧的 GCC 版本(4.4.1 之前的某个地方)显示出对齐问题。

注 1:我提出的代码没有回答我理解为“对齐数组的每个字段”的问题。

注意 2:在 main() 中引入非静态 a[] 并使用 GCC 3.4.6 进行编译会破坏结构数组的对齐指令,但结构之间的距离保持 0x1000 ......仍然很糟糕!(有关解决方法,请参阅@zifre 答案)

于 2010-11-12T17:05:38.220 回答