web-dev-qa-db-ja.com

スタック変数はGCC __attribute __((aligned(x)))によってアライメントされていますか?

私は次のコードを持っています:

_#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)))は何をしますか? this 説明を誤解しましたか?

Gcc 4.1.2を使用しています。

86
cojocar

問題は、アレイがスタック上にあることだと思います。関数の起動時にスタックポインタは何でもかまいませんので、必要以上に多くを割り当てて調整せずに配列を整列させる方法はありません。配列を関数からグローバル変数に移動すると、機能するはずです。他にできることは、ローカル変数として保持することです(これは非常に良いことです)が、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

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

94
Zifre

Gccにバグがあり、スタック変数で動作しないようにattributeが調整されました。以下にリンクされているパッチで修正されているようです。以下のリンクには、問題に関するかなりの議論も含まれています。

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

上記のコードをgccの2つの異なるバージョンで試してみました: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のバグを見つけたようですが、それは後のバージョンで修正されたようです。

40
rts1

最近の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
13
Caleb Case

アライメントはすべてのタイプに効果的ではありません。構造を使用して、動作中の属性を確認することを検討する必要があります。

#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ケースのコメントに続いて、最初の問題は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:main()内に非静的a []を入れてGCC 3.4.6でコンパイルすると、構造体の配列のアライメントディレクティブが壊れますが、構造体間の距離は0x1000のままになります...まだ悪い! (回避策については、@ zifreの回答を参照してください)

9
levif