これらのコードを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
ステートメントの前にすべてのポインターを宣言する必要があります。つまり、現時点では必要ない場合でも、これらのポインターを宣言する必要があります(およびいくつかの原則による違反)。
g++
が便利なtail-gotoステートメントを禁止しているというOriginの設計上の考慮事項は何ですか?
更新:
goto
は、変数(任意の型ポインターに限定されない変数の)宣言をクロスできますただし、初期値を取得したものを除く。上記のNULL
割り当てを削除すると、g++
は今は黙っています。したがって、goto
- cross-areaの間にある変数を宣言する場合は、しないでくださいそれらを初期化します(まだいくつかの原則に違反しています)。
初期化が実行されると重要でない初期化を持つオブジェクトの存続期間が開始されるため、ジャンプの後にそれぞれのオブジェクトが存在しないため、Gotoは変数の初期化をスキップできません。
C++ 11§3.8/ 1:
[…]タイプTのオブジェクトの存続期間は、次の場合に始まります。
タイプTの適切な配置とサイズのストレージが取得されます。
オブジェクトに重要な初期化がある場合、その初期化は完了です。
C++ 11§6.7/ 3:
ブロックに転送することは可能ですが、初期化で宣言をバイパスする方法ではできません。自動ストレージ期間を持つ変数がスコープ内にないポイントからスコープ内にあるポイントにジャンプするプログラムは、変数がスカラー型、自明なデフォルトコンストラクターおよび自明なデストラクタを持つクラス型でない限り、形式が正しくありません。これらのタイプのいずれかのcv修飾バージョン、または前述のタイプのいずれかの配列であり、初期化子なしで宣言されています(8.5)。
エラーは[-fpermissive]
について言及しているため、そのコンパイラフラグを指定することで警告に変えることができます。これは2つのことを示しています。以前は許可されていました(変数は存在しますが、ジャンプ後は初期化されていません)。また、gcc開発者は仕様で禁止されていると考えています。
コンパイラーは、変数を使用するかどうかではなく、変数を初期化する必要があるかどうかのみをチェックします。それ以外の場合、結果の一貫性は失われます。ただし、変数が不要になった場合は、自分でその寿命を終了させて、「末尾移動」を実行可能にすることができます。
int main() {
int a =0;
goto exit;
{
int *b = NULL;
}
exit:
return 0;
}
完全に有効です。
補足として、ファイルの拡張子は.c
であり、C++ではなくCであることを示しています。 g++
ではなくgcc
を使用してコンパイルする場合、Cにはその制限がないため、元のバージョンをコンパイルする必要があります(Cには可変長配列のみの制限があります。これには、 C++です)。
int
などのプリミティブ型には簡単な回避策があります。
// --- original form, subject to cross initialization error. ---
// int foo = 0;
// --- work-around form: no more cross initialization error. ---
int foo; foo = 0;