32

これらのコードが次のようにコンパイルされているとしg++ます。

#include <stdlib.h>

int main() {
    int a =0;

    goto exit;

    int *b = NULL;

exit:
    return 0;
}

g++エラーをスローします:

goto_test.c:10:1: error: jump to label ‘exit’ [-fpermissive]
goto_test.c:6:10: error:   from here [-fpermissive]
goto_test.c:8:10: error:   crosses initialization of ‘int* b’

gotoはポインタ定義をクロスできないようですが、gccコンパイルしても問題ありません。何も文句はありません。

エラーを修正した後、ステートメントの前にすべてのポインターをgoto宣言する必要があります。つまり、現時点では必要ない場合でも、これらのポインターを宣言する必要があります(一部の原則に違反します)。

有用なtail-gotoステートメントをg++禁止した起源設計の考慮事項は何ですか?


アップデート:

goto変数(ポインターに限定されない任意のタイプの変数)宣言をクロスできますが、初期化値を取得した宣言は除きますNULL上記の割り当てを削除する場合は、ここでg++黙ってください。したがって、goto-cross-area間で変数を宣言する場合は、それらを初期化しないでください(それでもいくつかの原則に違反します)。

4

2 に答える 2

42

ジャンプ後にそれぞれのオブジェクトが存在しないため、Gotoは変数の初期化をスキップできません。これは、初期化が重要なオブジェクトの存続期間は、その初期化が実行されたときに開始されるためです。

C++11§3.8/1:

[…]タイプTのオブジェクトの存続期間は、次の場合に始まります。

  • タイプTの適切な配置とサイズのストレージが取得され、

  • オブジェクトに自明でない初期化がある場合、その初期化は完了です。

C++11§6.7/3:

ブロックに転送することは可能ですが、初期化で宣言をバイパスする方法ではできません。自動保存期間を持つ変数がスコープ内にないポイントからスコープ内にあるポイントにジャンプするプログラムは、変数がスカラー型、単純なデフォルトコンストラクターと単純なデストラクタを持つクラス型を持たない限り、形式が正しくありません。これらのタイプの1つのcv修飾バージョン、または前述のタイプの1つの配列であり、初期化子なしで宣言されます(8.5)。

エラーにはが記載されているので、[-fpermissive]そのコンパイラフラグを指定することで警告に変えることができます。これは2つのことを示しています。以前は許可されていた(変数は存在しますが、ジャンプ後に初期化されない)こと、およびgcc開発者は仕様で禁止されていると信じています。

コンパイラーは、変数を初期化する必要があるかどうかだけをチェックし、変数を使用するかどうかはチェックしません。そうしないと、結果に一貫性がなくなります。しかし、変数がもう必要ない場合は、自分でその存続期間を終了して、「tail-goto」を実行可能にすることができます。

int main() {
    int a =0;
    goto exit;
    {
        int *b = NULL;
    }
exit:
    return 0;
}

完全に有効です。

ちなみに、ファイルの拡張子は.c、C++ではなくCであることを示しています。gccの代わりにを使用してコンパイルする場合g++、Cにはその制限がないため、元のバージョンをコンパイルする必要があります(C ++にはまったく存在しない可変長配列の制限のみがあります)。

于 2013-01-11T08:31:04.537 に答える
4

次のようなプリミティブ型には簡単な回避策がありますint

 // ---  original form, subject to cross initialization error.  ---
 // int foo = 0;

 // ---  work-around form: no more cross initialization error.  ---
 int foo;  foo = 0;
于 2015-09-02T11:04:53.700 に答える