web-dev-qa-db-ja.com

C:スタックメモリ、goto、および「可変的に変更されたタイプの識別子のスコープにジャンプ」、

私はこれがコンパイルを拒否することを発見しました:

int test_alloc_stack(int size){
    if(0) goto error; // same issue whatever conditional is used
    int apply[size];
    give_values(apply,size);
    return 1;
    error:
        return 0;
}

私が得るエラーは次のとおりです:「可変的に変更されたタイプの識別子のスコープにジャンプします」。 「goto」で行を削除し、エラーにジャンプすると、問題が解決します。

適用に動的割り当てを使用すると、問題も解消されます。これは正常にコンパイルされます:

 int test_alloc_heap(int size){
    if(0) goto error;
    int * apply = calloc(sizeof(int),size);
    give_values(apply,size);
    free(apply);
    return 1;
    error : return 0;
}

何が起こっている ?

23
Vince

宣言:

int apply[size];

可変長配列を作成します。スコープ外になると、コンパイラはその配列の割り当てをクリーンアップするコードを生成する必要があります。このようなオブジェクトのスコープにジャンプすることは禁止されています。一部の実装では、クリーンアップコードに必要な初期化を調整する必要があり、スコープにジャンプすると初期化がバイパスされるためです。

動的割り当てに変更した場合、コンパイラーではなく、初期化とクリーンアップがユーザーの責任になります。

24
Michael Burr

それは標準によって禁止されています:

C99標準、パラグラフ6.8.6.1

制約

[...] gotoステートメントは、可変的に変更された型を持つ識別子のスコープの外側からその識別子のスコープの内側にジャンプしてはなりません。

これはまさにあなたのgotoが行っていることです。つまり、applyのスコープの外側から内側にジャンプします。

次の回避策を使用して、applyの範囲を制限できます。

if(0) goto error;

{
    int apply[size];
    give_values(apply,size);
    return 1;
}

error:
return 0;
19
user703016

gotoを使用すると、(実行時に)applyを割り当てる行をスキップできます。

次の4つの方法のいずれかで問題を解決できます。

1:gotoを使用しないようにコードを書き直します。

2:applyの宣言をgotoの前に移動します。

3:スコープを変更してerror:applyの範囲外です:

int test_alloc_stack(int size){
    if(0) goto error; // same issue whatever conditional is used
    {
        int apply[size];
        give_values(apply,size);
        return 1;
    }
    error:
        return 0;
}

4:変数宣言を変更して、コンパイル時にサイズを決定できるようにします。

7
Klas Lindbäck