2012-02-22 6 views
7

は、以下のコードを検討:x86のためのものであるとしてコンパイルなぜGCCは異なるスコープのローカルユニオンに別々のスタックスペースを割り当てるのですか?

#include <stdlib.h> 

#ifndef TRY 
#define TRY struct 
#endif 

TRY testme 
{ 
    int one; 
    int two; 
    char three; 
    int four; 
}; 

int 
main (void) 
{ 
    { 
    volatile TRY testme one; 

    one.one = 2; 
    one.three = 7; 
    } 

    { 
    volatile TRY testme twos; 

    twos.one = 3; 
    } 

    { 
    volatile TRY testme one; 

    one.one = 4; 
    } 

    { 
    volatile TRY testme twos; 

    twos.one = 5; 
    } 

    { 
    volatile TRY testme twos; 

    twos.one = 6; 
    } 

    { 
    volatile TRY testme twos; 

    twos.one = 6; 
    } 

    return EXIT_SUCCESS; 
} 

は(意味testmeが構造体である)であり、コンパイラは主に割り当てるスタックサイズは16バイトです。

$ gcc -g -O2 test.c -o test 
$ objdump -d ./test | ./checkstack.pl i386 | grep main 
16 main 

しかし、(testmeが組合であることを意味する)組合に定義TRYでコンパイルされ、コンパイラは主に割り当てるスタックサイズは32バイトである:また

$ gcc -DTRY=union -g -O2 test.c -o test 
$ objdump -d ./test | ./checkstack.pl i386 | grep main 

、構造体の任意の追加のインスタンス追加のスコープで定義された/共用体は、共用体を使用するときより大きなスタック割り当てを生成しますが、構造体として使用されるときはスタック割り当てを拡大しません。

これは意味をなさない - ユニオンはスタック領域を減らす必要はありません。それ以上の場合は、同じフィールドを持つ構造体です。

GCCは異なるスコープであっても同時に使用されているように見えるが、構造体では同じことをしないように見える。

いくつかのより多くの明確化:

  1. 揮発性は、割り当てを離れて最適化から、コンパイラを停止するために使用されます。 volatileを取り除き、最適化を行わずにコンパイルすると同じ結果が得られます。

  2. testmeがメンバの1つとして共用体を持つ構造体であっても、同じ動作が観察されます。言い換えれば、構造体のメンバの1つがGCCのスタック割り当てのための共用体であれば十分です。

  3. コンパイラはgccバージョン4.4.3(Ubuntu 4.4.3-4ubuntu5)ですが、他のアーキテクチャ用の他のGCCバージョンも同じ動作を示しました。

  4. checkstack.plは、スタックを割り当てるために使用される命令(スタックポインタへのサブ)のobjdump出力を単に検索します。

私の質問:

  1. なぜGCCがこれを行うのですか?これはバグですか、この動作の理由はありますか?
  2. これはバグではないと仮定して、これを回避し、GCCに構造体のスタックをユニオンと同じように割り当てる方法があります。

明確化:構造体または共用体は、その部分のサイズからサイズが大きくなるように見えるなぜ私の質問はありません。私は理由が整列のためのパディングであることを理解します。私の問題は、コンパイラは異なるスコープで定義されているにもかかわらず、同じフィールドを持つ構造体では同じことをしないにもかかわらず、複数のスタックフレームを複数のスタックのインスタンスに割り当てることです。

ありがとうございます!

+0

はたぶん組合は、同様に(まだ) '-O2'で最適化されていませんか?生成されたアセンブラコードを見てください。また、 'strict-aliasing'を無効にしてみてください。ああ、現在のGCCを試してみてください。 –

+1

[この質問](http://stackoverflow.com/questions/8453881/sizeof-union-larger-than-expected-how-does-type-alignment-take-place-here)と[この1つ](http://stackoverflow.com/questions/7212585/why-is-my-unions-size-bigger-than-i-expected)?それはパディングとアライメントの制約のようです。 – Coren

+0

@ Anony-Mousse -fno-strict-aliasingは同じ結果をもたらします。私は最新のGCCをテストしてテストしています。 – gby

答えて

8

少なくとも、gccの厳密なエイリアシング・パラノイアを組合について緩和させる試みがなされているようです。

あなたはgccのソースは、あなたがこのまたは同等のパッチが適用されたからコンパイルしていることを確認したいことがあります。 http://codereview.appspot.com/4444051/

+0

これはまさに私が探していたものです。どうもありがとうございます :-) – gby

0

Intのパディングと基本的なデフォルトサイズの定義のようです。

32ビットのメモリマップは次のようになります つ(2バイト) 2つ(2バイト) スリー(1バイト) (1バイト)パディング 4(2バイト)

合計 - 8バイト。 64ビットで

それは次のようになります つ(4バイト) 2つ(4バイト) スリー(1バイト) (3バイトのパディング) フォー(4バイト)

合計 - 16バイト。

"int"を "short int"に変更すると、メモリの外観が変わります。

+1

しかし、 'union'として、32バイトか64ビットかにかかわらず、すべてが4バイトしか取らないようにしてください。 –

+0

私は単一の構造体または共用体がどれだけかかるかに問題はありません。私の問題は、GCCが異なるスコープで定義された共用体の異なるインスタンスに対して異なるスタック領域を割り当てているようだが、それを行う必要はなく、実際には構造体のためにそれをしないということだ。 – gby

+0

それは次のものに依存する。もしそれが組合の配列の中にあれば、それは64ビットの下でも32ビットを取るでしょう。それが可変であれば、64ビットシステムでは64ビットにパッディングされます。それは範囲ではなく、「次のものは何か」です。 –

関連する問題