14

昨日、私は次のようなコードを書いていることに気づきました。

SomeStruct getSomeStruct()
{
    SomeStruct input;

    cin >> input.x;
    cin >> input.y;
}

もちろん、作成したばかりの構造体を実際に返すのを忘れています。奇妙なことに、この関数によって返された構造体の値はゼロに初期化されました (g++ を使用してコンパイルした場合)。これは単なる偶然ですか、それとも別の SomeStruct が作成され、どこかで暗黙的に初期化されましたか?

4

6 に答える 6

22

(明示的に値を返さずに) 値を返すように宣言された関数の末尾から外れると、未定義の結果が生じます。gcc の場合、-Wall最も有用な警告をオンにするコマンド ライン スイッチから開始する必要があります。必要な警告を制御する特定の gcc 警告は次のとおりです-Wreturn-type(これは に含まれてい-Wallます。完全を期すためにこれについて言及しただけです)。

警告をオンにしたら、-Werror警告をエラーとして扱い、エラーを検出した時点でビルドを停止するためにも使用する必要があります。

于 2008-11-16T04:53:26.500 に答える
9

最近のほとんどの CPU アーキテクチャの呼び出し規約では、特定のレジスタを指定して、関数の戻り値を呼び出し元に渡します。呼び出し元は関数呼び出しを行い、指定されたレジスタを戻り値として使用します。明示的に値を返さない場合でも、呼び出し元はそのレジスターにたまたまゴミが入っているものを使用します。

コンパイラは、関数内の内部計算に使用できるすべてのレジスタも使用します。戻り値を保持するために指定されたレジスタは、関数内のその他の計算にも使用されます。したがって、戻り値を指定するのを忘れた場合、奇跡的に正しい値が呼び出し元に返されていることに気付くことは珍しくありません。コンパイラはそのレジスタを使用してオブジェクトを格納しました。

残念ながら、関数の些細な変更でもレジスタ割り当てが変更される可能性があるため、戻り値は真のガベージになります。

于 2008-11-16T05:34:13.143 に答える
7

別の SomeStruct が作成され、どこかで暗黙的に初期化されましたか?

構造体がどのように返されるかを考えてください。xとの両方yが 32 ビットの場合、大きすぎて 32 ビット アーキテクチャのレジスタに収まらず、64 ビット アーキテクチャの 64 ビット値にも同じことが当てはまります (@Denton Gentry の回答では、より単純な値が返される方法について言及しています)。 、したがって、どこかに割り当てる必要があります。これにヒープを使うのはもったいないので、スタックに割り当てる必要があります。getSomeStructただし、関数が返された後は無効になるため、関数のスタック フレームには配置できません。

代わりに、コンパイラは呼び出し元に、呼び出された関数に割り当てられたスペースへの隠しポインターを渡すことにより、呼び出し元の関数に結果を配置する場所 (おそらく呼び出し元のスタックのどこかにある) を伝えます。したがって、ゼロに設定されている場所は、関数ではなくcallergetSomeStructにあります。

余分なコピーを省略できる「名前付き値の戻り値の最適化」などの最適化もあります。したがって、欠落している を使用したreturn場合、結果は、一時的なものを作成してコピーするのではなく、呼び出し元によって割り当てられたスペースに直接作成されます。

何が起こっているのかをもっと知るには、呼び出し元の関数を調べる必要があります。SomeStruct後で関数の戻り値を割り当てる「空」を(ゼロに)初期化していますgetSomeStructか?それとも何か他のことをしていますか?

于 2008-11-16T06:15:19.957 に答える
3

これは面白いと思います。デフォルトのオプションを使用すると、次のコンパイラはGetSomeStruct()関数をコンパイルするときに次のように動作します。

  • Microsoft VC、すべてのバージョン (とにかく VC6 以降):

    error C4716: 'getSomeStruct' : must return a value

  • デジタル火星:

    Warning 18: implied return of getSomeStruct at closing '}' does not return value

  • コモー:

    warning: missing return statement at end of non-void function "getSomeStruct"

  • gcc:

    エラーまたは警告なし

標準 (6.6.3 パラグラフ 2) から次の 2 つの文を考えると、次のようになります。

式のない return ステートメントは、値を返さない関数、つまり戻り値の型が void の関数、コンストラクタ (12.1)、またはデストラクタ (12.4) でのみ使用できます。... 関数の最後を流れることは、値のない戻りと同等です。これにより、値を返す関数で未定義の動作が発生します。

この場合、コンパイラがエラーを出さない理由はほとんどないと思います。多くのコンパイラが警告のみを表示するか、診断をまったく表示しないのはなぜですか?

于 2008-11-16T18:15:17.710 に答える
2

私にとって、コンパイラはそれを許可しませんでした: http://codepad.org/KkzVCesh

于 2008-11-16T04:38:41.367 に答える
1

電源を入れていないため、警告は表示されませんでし-Wall -Werrorた。(他の回答で述べたように)

ただし、スタック オブジェクトが呼び出し元関数でデフォルトで構築されているため、おそらく明示的なゼロ引数を使用して、またはスタック上のゼロが原因で、おそらく結果としてゼロで満たされた構造体を取得したと思いますか?

于 2008-11-16T09:58:00.100 に答える