GCC -Wuninitialized / -Wmaybe-初期化されていない問題

2013年01月03日に質問されました。  ·  閲覧回数 20.6k回  ·  ソース

user593062 picture
2013年01月03日

gcc-4.7 (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2を使用すると非常に奇妙な問題が発生します。 警告なしに次の有効なコードをコンパイルできません。

extern void dostuff(void);

int test(int arg1, int arg2)
{
    int ret;

    if (arg1) ret = arg2 ? 1 : 2;

    dostuff();

    if (arg1) return ret;

    return 0;
}

コンパイルオプションと出力:

$ gcc-4.7 -o test.o -c -Os test.c -Wall
test.c: In function ‘test’:
test.c:5:6: warning: ‘ret’ may be used uninitialized in this function [-Wmaybe-uninitialized]

ただし、次のコードは警告なしでコンパイルされます(アセンブリの効率は少し低下しますが)。

extern void dostuff(void);

int test(int arg1, int arg2)
{
    int ret;

    if (arg1 && arg2) ret = 1;
    if (arg1 && !arg2) ret = 2;

    dostuff();

    if (arg1) return ret;

    return 0;
}

私はやや立ち往生しており、これをコンパイラのバグと考えています。 何かご意見は?

回答

Alok Save picture
2013年01月03日
18

実際、これはgccの既知の問題です。
gccは、初期化さです
欠点は適切に指摘されており、欠点を克服するためのイニシアチブがあります。
より良い初期化されていない警告

GNUコンパイラコレクションは、オプション-Wuninitialized使用した初期化されていない変数の使用について警告します。 ただし、現在の実装にはいくつかの認識された欠点があります。 一方では、一部のユーザーは、より詳細で一貫性のある警告を望んでいます。 一方、一部のユーザーは、警告をできるだけ少なくしたいと考えています。 このプロジェクトの目標は、両方の可能性を実装すると同時に、現在の機能を改善することです。

このイニシアチブは、より良い警告を提供することを目的としており、あなたのケースと同様のケースの例を引用しています。 関連する部分は次のとおりです。

ユーザーが誤検知として理解する内容は、特定のユーザーによって異なる場合があります。 一部のユーザーは、現在の環境と組み合わされたオプティマイザーのアクションのために隠されているケースに興味を持っています。 ただし、コンパイルされたコードでは発生しないため、そのケースは非表示になっているため、多くのユーザーはそうではありません。 正規の例は

int x;
if (f ())
     x = 3;
return x;

ここで、「f」は現在の環境に対して常にゼロ以外を返すため、最適化することができます。 ここで、ユーザーのグループは、「f」が他の場所でコンパイルされたときにゼロを返す可能性があるため、初期化されていない警告を受け取りたいと考えています。 それでも、他のユーザーグループは、コンパイル中の実行可能ファイルでは発生しない状況についての誤った警告を検討します。

Mecki picture
2020年12月25日
0

その間にgccが修正されたかどうかはわかりません。 そうでない場合は、 clangを試してみてください。 これははるかに優れたコンパイラIMHOであり、はるかに優れたコード分析を実行します。

一部のコメントがコンパイラが正しいと主張しているという理由だけで、 retは初期化されていない状態で使用される可能性があります。これは、反対の証拠です。 コード

int test(int arg1, int arg2)
{
    int ret;
    if (arg1) ret = arg2 ? 1 : 2;
    dostuff();
    if (arg1) return ret;
    return 0;
}

2つの同一のifステートメントを1つに結合するだけで、次のコードに簡単に変換できます。

int test(int arg1, int arg2)
{
    if (arg1) {
        int ret = arg2 ? 1 : 2;
        dostuff();
        return ret;
    }
    dostuff();
    return 0;
}

これは同等のコードであり、 retを初期化せずに使用すること

しかし、繰り返しになりますが、コードはさらに単純化できます。

int test(int arg1, int arg2)
{
    dostuff();
    return (arg1 ? (arg2 ? 1 : 2) : 0);
}

問題は解決しました。 retはなくなりました。