次のコードを検討してください。
#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;
}
x86 用にそのままコンパイルすると (testme が構造体であることを意味します)、コンパイラが main に割り当てるスタック サイズは 16 バイトです。
$ gcc -g -O2 test.c -o test
$ objdump -d ./test | ./checkstack.pl i386 | grep main
16 main
ただし、ユニオンに定義された TRY (testme がユニオンであることを意味する) でコンパイルされた場合、コンパイラがメインに割り当てるスタック サイズは 32 バイトです。
$ gcc -DTRY=union -g -O2 test.c -o test
$ objdump -d ./test | ./checkstack.pl i386 | grep main
さらに、追加のスコープで定義された構造体/共用体の追加のインスタンスは、共用体を使用するとより大きなスタック割り当てを生成しますが、構造体として使用するとスタック割り当てを拡大しません。
さて、これは意味がありません - 共用体は、同じフィールドを持つ構造体よりも少ないスタック スペースを取る必要があります。
GCC は、スコープが異なる場合でも共用体を同時に使用されるものとして扱うように見えますが、構造体に対しては同じことを行いません。
いくつかの詳細な説明:
volatile は、コンパイラが割り当てを最適化するのを止めるために使用されます。volatile を失い、最適化なしでコンパイルすると、同じ結果が得られます。
testme が共用体をメンバーの 1 つとして持つ構造体であっても、同じ動作が観察されます。言い換えれば、構造体のメンバーの 1 つが GCC の共用体であり、個別のスタック割り当てを行うことができれば十分です。
コンパイラは gcc バージョン 4.4.3 (Ubuntu 4.4.3-4ubuntu5) ですが、他のアーキテクチャ用の他の GCC バージョンでも同じ動作を示しました。
checkstack.pl は、objdump の出力から、スタック (スタック ポインターのサブ) の割り当てに使用される命令を検索するだけです。
私の質問:
- なぜGCCはこれを行うのですか? これはバグですか、それともこの動作には理由がありますか?
- これがバグではないと仮定すると、これを回避し、GCC にユニオンと同じスタクトのスタックを強制的に割り当てる方法はありますか。
明確化:私の質問は、構造体または共用体のサイズがその部分のサイズよりも大きく見える理由ではありません。その理由は、位置合わせのためのパディングであることを理解しています。私の問題は、コンパイラが、異なるスコープで定義されているにもかかわらず、ユニオンの異なるインスタンスに複数のスタック フレームを割り当てることですが、同じフィールドを持つ構造体に対して同じことを行うべきではなく、実際には行いません。
ありがとう!