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。これにより、スタックに格納されなくなります。配列のコピーは 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

これは期待されるものです。元のコードでは、あなたのようにランダムな値を取得します。

于 2009-05-08T19:41:05.947 に答える
42

gcc にバグがあり、アトリビュートアラインメントがスタック変数で機能しませんでした。以下のリンクのパッチで修正されたようです。以下のリンクには、問題に関するかなりの議論も含まれています。

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

上記のコードを 2 つの異なるバージョンの 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 バージョンのみが原因です。リクエスタのソースコードを使用して、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: 非静的 a[] を main() 内に取り込み、GCC 3.4.6 でコンパイルすると、構造体の配列のアラインメント ディレクティブが壊れますが、構造体間の距離は 0x1000 のままです...それでも悪いです! (回避策については、@zifre の回答を参照してください)

于 2010-11-12T17:05:38.220 に答える