gcc と cl のスタックフレームの作り方・使い方の違い

3 ヶ月ぐらい、デスクトップの附箋メモに書きっぱなしで、調べいなくて、今後も調べないような気がするので、ここにメモを残して附箋は削除しよう。

#include <stdio.h>

int foo(int& b) {
    printf("%p %d\n", &b, b);
    return b *= 2;
}

int& bar() {
    int a = 1;
    printf("%p %d\n", &a, a);
    return a;
}

int main() {
    return printf("%d\n", foo(bar()));
}

こんな感じのコードがあって、これを gcc と cl でコンパイルすると、両方ともコンパイル時に警告は出るもののコンパイルが完了する。

gcc の警告は以下。

ref.cpp: In function `int& bar()':
ref.cpp:9: warning: reference to local variable `a' returned

cl の警告は以下。

warning C4172: ローカル変数またはテンポラリのアドレスを返します。

実行結果は gcc が以下。

0028FEF4 1
0028FEF4 1
2

cl が以下。

0018FF34 1
0018FF34 1638208
3276416


一応これらは期待する値だったりそうでなかったりしつつまともに動いているんだけど、以下のようにコードを変更すると、cl ではアプリが不正終了する。

#include <stdio.h>

int foo(int a, int& b) {
    printf("%p %d\n", &b, b);
    return b *= 2;
}

int& bar() {
    int a = 1;
    printf("%p %d\n", &a, a);
    return a;
}

int main() {
    return printf("%d\n", foo(bar(), bar()));
}

foo 内でスタックを壊して、戻り先アドレスを書き換えちゃったりしてるのかもしれない。

調べたかったのは gcc に関して、呼び出し方とか、引数の数とか、ローカル変数の数とかを変更しても、それでも期待通りに動作するのはどんなスタックフレームの管理をしているのか、という点。cl は割と素直に、関数の呼出し毎にスタックフレームを積んでは壊してを繰り返してるような感じだけど。

アセンブラを吐いてみればわかると思うんだけど、それが 3 ヶ月間できてない。