1. ホーム
  2. c

[解決済み] GCCの__attribute__((aligned(x)))でスタック変数がアライメントされるのか?

2023-04-13 20:10:03

質問

次のようなコードがあります。

#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を使っています。

どのように解決するのですか?

配列がスタック上にあることと、コンパイラが古すぎてオーバーアラインされたスタック変数をサポートしていないことが問題だと思います。 GCC 4.6 およびそれ以降 はそのバグを修正しました。 .

C11/C++11 alignas(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 . こうすることで、スタックに格納されるのを防ぐことができます。配列のコピーは1つだけなので、これらの方法は両方ともスレッドセーフでも再帰セーフでもないことに注意してください。

このコードで

#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

というのが期待されるものです。あなたのオリジナルのコードでは、私はちょうどあなたがしたようにランダムな値を取得します。