堆栈变量是否由GCC __attribute __((aligned(x)))对齐?

coj*_*car 86 c gcc callstack memory-alignment

我有以下代码:

#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]);
}
Run Code Online (Sandbox Code Playgroud)

我有以下输出:

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac
Run Code Online (Sandbox Code Playgroud)

为什么地址a[0]不是多个0x1000

到底__attribute__((aligned(x)))是什么?我误解了这个解释?

我正在使用gcc 4.1.2.

Zif*_*fre 96

我相信问题是你的阵列在堆栈上.因为当函数启动时堆栈指针可以是任何东西,所以没有办法对齐数组而不分配比你需要的更多并调整它.如果将数组移出函数并移入全局变量,它应该可以工作.你可以做的另一件事是将它保存为局部变量(这是一件非常好的事情),但要做到alignas(64) float a[4];.这将防止它存储在堆栈中.请注意,这两种方式都不是线程安全的或递归安全的,因为只有一个数组副本.

使用此代码:

#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]);
}
Run Code Online (Sandbox Code Playgroud)

我明白了:

0x804c000 0x804c004 0x804c008 0x804c00c
Run Code Online (Sandbox Code Playgroud)

这是预期的.使用原始代码,我只需获得与您相同的随机值.

  • +1正确答案.另一种解决方案是使本地阵列静态.堆栈上的对齐总是一个问题,最好养成避免它的习惯. (11认同)
  • 请注意,将其设置为静态也会使其不可重入且非线程安全. (3认同)
  • 即使在堆栈上,gcc 4.6+也能正确处理. (3认同)

rts*_*ts1 41

gcc中有一个错误导致属性对齐,无法使用堆栈变量.它似乎与下面链接的补丁一起修复.下面的链接也包含对该问题的大量讨论.

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

我已经尝试了上面的代码,使用了两个不同版本的gcc:4.1.2来自RedHat 5.7框,并且它与你的问题类似地失败(本地数组在0x1000字节边界上没有对齐).然后我在RedHat 6.3上用gcc 4.4.6尝试了你的代码,它运行完美(本地数组已经对齐).神话电视的人有一个类似的问题(上面的gcc补丁似乎修复):

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

无论如何,看起来你在gcc中发现了一个错误,似乎在以后的版本中修复了.

  • 根据链接的bug,gcc 4.6是第一个完全针对所有架构修复此问题的版本. (3认同)

小智 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]);
}
Run Code Online (Sandbox Code Playgroud)

我明白了:

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c
Run Code Online (Sandbox Code Playgroud)


lev*_*vif 9

Alignement对所有类型都无效.您应该考虑使用结构来查看操作中的属性:

#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]);
}
Run Code Online (Sandbox Code Playgroud)

然后,你会读到:

0x603000 0x604000 0x605000 0x606000
Run Code Online (Sandbox Code Playgroud)

这是你期待的.

编辑: 由@yzap推及以下@Caleb此案发表评论,最初的问题是由于GCC版本.我已经使用请求者的源代码检查了GCC 3.4.6 vs GCC 4.4.1:

$ ./test_orig-3.4.6
0x7fffe217d200 0x7fffe217d204 0x7fffe217d208 0x7fffe217d20c
$ ./test_orig-4.4.1
0x7fff81db9000 0x7fff81db9004 0x7fff81db9008 0x7fff81db900c
Run Code Online (Sandbox Code Playgroud)

现在很明显,较旧的GCC版本(4.4.1之前的某个地方)显示了对齐病理.

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

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

  • 正如zifre所说,它不是类型,而是你在你的版本中使它静止的事实. (2认同)