スタックメモリが使用されていないときに割り当てられるのはなぜですか?


14

次の例を考えてみます。

struct vector {
    int  size() const;
    bool empty() const;
};

bool vector::empty() const
{
    return size() == 0;
}

生成されたアセンブリコードvector::empty(clangによる最適化あり):

push    rax
call    vector::size() const
test    eax, eax
sete    al
pop     rcx
ret

なぜスタックスペースを割り当てるのですか?一切使用しておりません。省略することができます。MSVCとgccの最適化ビルドもこの関数にスタックスペースを使用するため(godboltを参照)、理由があるはずです。pushpop


7
暗黙のthisパラメータを説明しましたか?
dan04

1
@Bob__:いいえ。なぜ私はなぜですか?vector::size()この例では、インライン化されていないことをシミュレートするように定義されていません。
グット博士

1
では、コンパイラが知らないものをどのように最適化できるでしょうか?
Bob__

1
@Bob__:の実装を知っていてもvector::size()、スタックフレームの割り当てや割り当てには関係がないと思いますvector::empty()。でempty()、ちょうどそれが何であれ、と呼ばれています。
グット博士

1
まあ、あなたは何かを返す関数を呼び出しているので、そのためのスペースが必要です(よくわからない場合)。
Bob__

回答:


11

スタックスペースを割り当てるため、スタックは16バイトにアラインされます。これは、戻りアドレスが8バイトを必要とするために必要であり、スタックを16バイトに揃えるために追加の8バイトのスペースが必要です。

スタックフレームの配置は、一部のコンパイラのコマンドライン引数で構成できます。

  • MSVCドキュメントによると、スタックは常に16バイトにアラインされています。これを変更できるコマンドライン引数はありません。godboltの例はrsp、関数の先頭から40バイトが差し引かれていることを示しています。これは、他の何かもこれに影響することを意味します。
  • clang-mstack-alignmentオプションはスタックの配置を指定します。文書化されていませんが、デフォルトは16のようです。これを8に設定すると、生成されたアセンブリコードからスタック割り当て(pushおよびpop)が消えます。
  • gcc-mpreferred-stack-boundaryオプションはスタックの配置を指定します。指定された値がNの場合、2 ^ Nバイトのアライメントを意味します。デフォルト値は4で、16バイトを意味します。これを3(つまり8バイト)に設定すると、スタック割り当て(subおよびaddfor rsp)は生成されたアセンブリコードから消えます。

godboltをチェックしてください


だからこそ、C ++の達人、専門家は常に警告しています:構造体/クラスのメンバーを最長/最大のサイズの順に並べます...この方法だけが正しく効率的です
ノック

@geza:ありがとう。私は他の2つのコンパイラについて調査を行い、それをあなたの答えに書きました。あなたはそれが好きですか?
グット博士

1
@ Dr.Gut:ありがとう、あなたは答えをより良く完全にしてくれました。スタックアライメントは通常、システムのABIで文書化されていることに注意してください(たとえば、一部のシステムでは、ここに文書があります:github.com/hjl-tools/x86-psABI/wiki/X86-psABI)。
ゲザ

@geza:ありがとう。
グット博士
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.