GCC はなぜループに入れるだけで未定義の動作を許可するように騙されるのでしょうか? 質問する

GCC はなぜループに入れるだけで未定義の動作を許可するように騙されるのでしょうか? 質問する

以下は意味不明ですが、問題なくコンパイルされますg++ -Wall -Wextra -Werror -Winit-self(GCC 4.7.2 および 4.9.0 でテストしました)。

#include <iostream>
#include <string>

int main()
{
  for (int ii = 0; ii < 1; ++ii)
  {
    const std::string& str = str; // !!
    std::cout << str << std::endl;
  }
}

マークされた行は!!未定義の動作を引き起こしますが、GCC では診断されません。ただし、このfor行をコメントアウトすると、GCC からエラーが発生します。

error: ‘str’ is used uninitialized in this function [-Werror=uninitialized]

ここで知りたいのは、なぜ GCC は簡単に騙されてしまうのかということです。コードがループ内にない場合、GCC はそれが間違っていることを認識します。しかし、同じコードを単純なループ内に置くと、GCC は理解できなくなります。これは私にとっては気になります。なぜなら、C++ で愚かな間違いを犯したときにコンパイラが通知してくれることにかなり頼っているのに、一見些細なケースでは通知されないからです。

ボーナストリビア:

  • std::string変更する場合はint そして最適化をオンにすると、GCC はループがあってもエラーを診断します。
  • 壊れたコードを でビルドすると-O3、GCC は文字列引数に null ポインターを使用して ostream 挿入関数を文字通り呼び出します。安全でないキャストを行わなければ null 参照から安全だと思っていたなら、考え直してください。

これについて GCC のバグを報告しました:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203- ここで何が間違っていたのか、そしてそれが同様の診断の信頼性にどのような影響を与える可能性があるのか​​を、さらに詳しく理解したいと思います。

ベストアンサー1

ここで、何が間違っていたのか、そしてそれが同様の診断の信頼性にどのような影響を与える可能性があるのか​​をより深く理解したいと思います。

Clangとは異なり、GCCには自己初期化参照を検出するロジックがないため、ここで警告を取得するには、初期化されていない変数の使用を検出するコードに依存しますが、これは非常に気まぐれで信頼性が低いです(初期化されていない警告の改善議論のため。

の場合、intコンパイラは、初期化されていないをintストリームに書き込んだと判断できますが、 の場合、std::string型の式std::stringconst char*それに含まれるの取得の間に抽象化のレイヤーが多すぎるため、GCC は問題を検出できません。

たとえば、GCC は、最適化を有効にしている限り、変数の宣言と使用の間にコードが少ない単純な例に対して警告を発します。

extern "C" int printf(const char*, ...);

struct string {
  string() : data(99) { }
  int data;
  void print() const { printf("%d\n", data); }
};

int main()
{
  for (int ii = 0; ii < 1; ++ii)
  {
    const string& str = str; // !!
    str.print();
  }
}

d.cc: In function ‘int main()’:
d.cc:6:43: warning: ‘str’ is used uninitialized in this function [-Wuninitialized]
   void print() const { printf("%d\n", data); }
                                           ^
d.cc:13:19: note: ‘str’ was declared here
     const string& str = str; // !!
                   ^

このような診断の欠如は、ヒューリスティックに頼って問題を検出する少数の診断にのみ影響すると思われます。これらは、次のような警告を出す診断です。5月初期化せずに使用する」または「5月「厳密なエイリアシング規則に違反しています」という警告や、「配列の添え字が配列の境界を超えています」という警告も表示される可能性があります。これらの警告は 100% 正確ではなく、ループなどの「複雑な」ロジックによって、コンパイラがコードの分析を諦め、診断を行えなくなる可能性があります。

私の意見では、解決策は、初期化の時点で自己初期化された参照のチェックを追加し、後で使用されるときに初期化されていないことを検出することに依存しないことです。

おすすめ記事