23

クラッシュのデバッグ中に、いくつかのコードでこの問題に遭遇しました。

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit;

    ...

err_exit:
    free(p2);
    free(p1);

    return -1;
}

この問題は、最初の malloc が失敗したときに発生します。の初期化にジャンプするためp2、ランダム データが含まれ、 への呼び出しfree(p2)がクラッシュする可能性があります。

これが、コンパイラーが goto が初期化を越えてジャンプすることを許可しない C++ と同じように扱われることを期待/希望します。

私の質問: 標準で許可されている初期化を飛び越えていますか、それとも gcc の c99 実装のバグですか?

4

7 に答える 7

17

を使用して変数定義を飛び越えたときに警告するように gcc に依頼できます。-Wjump-misses-init次に、 を使用して-Werror(または、より正確には-Werror=jump-misses-initを使用して)、ユーザーにその処理を強制することができます。この警告は-Wc++-compat、gcc 開発者が C と C++ でコードの動作が異なることを認識できるようにするために含まれています。

コードを少し変更することもできます。

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit_1;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit_2;

    ...

err_exit_2:
    free(p2);
err_exit_1:
    free(p1);

    return -1;
}

...そして、ラベルと初期化された変数のペアを維持するだけです。初期化された変数を使用して他の多くの関数を呼び出すと、同じ問題が発生します。無料はたまたまより明白なものです。

于 2010-05-12T18:35:07.873 に答える
9

そのようなジャンプは実際に標準で許可されているため、これは GCC のバグではありません。規格では、この状況を附属書 I に推奨される警告として挙げています。

スコープに関して C99 でのジャンプに課される唯一の制限は、VLA のような可変的に変更された型の変数のスコープにジャンプすることは違法であるということです。

int main() {
  int n = 5;
  goto label; // <- ERROR: illegal jump
  int a[n];
label:;
}

つまり、「ジャンプは C のジャンプにすぎない」というのは正しくありません。ジャンプは、C++ ほど厳密ではありませんが、変数スコープに入るときに多少制限されます。あなたが説明する状況は、制限されたものではありません。

于 2010-05-12T19:11:24.500 に答える
4

うーん、それは新しい標準がどこでも変数宣言を許可しているからではなく、それを使用することが常に良い考えです。あなたの場合、私は古典的なCでそれをしたいと思います。

int func()
{
char *p1 = NULL;    /* So we have a defined value */
char *p2 = NULL;

  p1 = malloc(...);
  if(!p1)
    goto err_exit;

  p2 = malloc(...);
  if(!p2)
    goto err_exit;

  ...

  err_exit:
    free(p2);
    free(p1);

  return -1;
}
于 2010-05-13T15:46:07.967 に答える
3

これは gcc のバグではありません。ジャンプは C の単なるジャンプです。特別なロジックは適用されません。問題は、ポインタをNULL最初に初期化していないことです。あなたがそうするなら、あなたはfree(NULL)クラッシュしないフリーコールになります. で機能を開始するとchar *p1 = NULL, *p2 = NULL;、すべてがうまくいきます。

于 2010-05-12T18:34:33.930 に答える
2

このコードを -O2 フラグでコンパイルすると

gcc -Wall -std=c99 -O2 jump.c

警告があります:

jump.c: In function ‘func’:
jump.c:10: warning: ‘p2’ may be used uninitialised in this function

最適化なしでは警告なし

于 2010-05-12T19:16:02.053 に答える
0

AndreyT が言うように、C99 では初期化を飛び越えることが許可されています。2 つの失敗に別のラベルを使用して、ロジックを修正できます。

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit_p1;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit;

    ...

err_exit:
    free(p2);
err_exit_p1:
    free(p1);

    return -1;
}

これは標準的なパターンです。「早期エラー」により、エラー終了コードの後半部分にジャンプします。

于 2010-05-12T23:40:56.440 に答える
-5

gotosを使用することは賢明なアイデアではなく、その理由の1つを見つけただけです。個々のエラーごとにエラー処理関数を呼び出す必要があります。

于 2010-05-12T19:07:15.057 に答える