[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]  


GCCの既知の不具合原因

このセクションでは、 GCCのユーザに影響を及ぼす既知の問題について説明します。 これらの問題のほとんどは、 それ自体はGCCのバグではありません。 もしバグであれば、 私たちはそれを修正したでしょう。 ただし、 これらの問題がユーザに対して及ぼす結果は、 バグがもたらす結果と同じようなものかもしれません。

これらの問題のいくつかは、 他のソフトウェアのバグによるものです。 また、 それを追加するにはあまりにも労力のかかるような機能の欠如が問題であることもあります。 さらに、 何が最善であるかについて人々の意見が一致しない領域が問題となることもあります。

未修正の本物のバグ

インストールの問題

以下は、 GCCをインストールする際に発生する問題 (および、 実際には何か問題があることを意味しているわけではない、 見せかけだけの問題) の一覧です。

クロス・コンパイラの問題

いくつかの理由により、 特定のマシン上においてクロス・コンパイル処理を行う際に問題に直面することがあるかもしれません。

Interoperation

相互作用

このセクションでは、 GNU CやGNU C++を他のコンパイラと一緒に使う場合や、 特定のシステム上のアセンブラ、 リンカ、 ライブラリ、 デバッガと一緒に使う場合に直面する様々な問題を一覧にして示します。

特定のプログラムのコンパイルにおける問題

プログラムによっては、 コンパイルに問題のあることがあります。

GCCの非互換性

GNU Cと既存の (ANSIに対応していない) ほとんどのCとの間には、 注目すべき非互換性がいくつかあります。 `-traditional'オプションを使うと、 GNU Cに対して他のCコンパイラと似た振る舞いをするよう指示が行われ、 これらの非互換性の多くが取り除かれます。 しかし、 すべての非互換性が取り除かれるわけではありません

修正されたヘッダ・ファイル

GCCは、 いくつかのシステム・ヘッダ・ファイルを訂正したものをインストールしておくことを必要とします。 これは、 ほとんどのターゲット・システムのヘッダ・ファイルには、 未修正のままではGCCと組み合わせて使うことができないものがあるからです。 バグを持つものもありますし、 ANSI Cと非互換のものもあります。 また、 他のコンパイラの特殊な特徴に依存しているものもあります。

GCCをインストールすると、 fixincludesと呼ばれるプログラム (あるいは、 あるターゲット・マシンでは、 例えばfixinc.svr4のような代わりのプログラム) を実行することにより、 修正されたヘッダ・ファイルが自動的に作成され、 インストールされます。 通常ユーザは、 このことに注意を払う必要はありません。 しかし、 このプログラムが自動的には正しいことをしてくれないケースもあります。

標準ライブラリ

GCCは、 ISO/ANSI C標準がconforming freestanding implementation (適合する自立した実装) と呼ぶところのものに、 単独でなることを試みています。 このことは、 すべてのANSI C言語の特徴だけでなく、 `float.h'`limits.h'`stdarg.h'`stddef.h'の内容すべてが利用可能であることを意味しています。 これ以外のCライブラリは、 オペレーティング・システムのベンダによって提供されます。 そのCライブラリがC標準に適合しないのであれば、 ユーザ・プログラムは予期しない警告を (特に`-Wall'を使っている場合に) 受けるかもしれません。

例えば、 Cの標準ではsprintf関数はintを戻り値とすることになっていますが、 SunOS 4.1.3上のsprintf関数はchar *を戻り値とします。 fixincludesプログラムが、 この関数のプロトタイプを標準に合うように変更することは可能でしょうが、 この場合でもその関数自体は依然としてchar *を返すのですから、 このような変更は正しくありません。

標準に準拠したライブラリが必要であれば、 それを自分で探す必要があります。 GCCは標準に準拠したライブラリを提供していません。 (glibcと呼ばれる)GNU Cライブラリは、 いくつかのオペレーティング・システムに移植されており、 ANSI/ISO、 POSIX、 BSD、 SystemVとの互換性を提供しています。 あるいは、 より新しいライブラリが利用可能になっているかどうかを、 オペレーティング・システムのベンダに問い合わせてみることもできるでしょう。

失望と誤解

以下の問題が存在することは残念ではありますが、 実際にこれを回避するための方法を私たちは1つも知りません。

GNU C++ に対する一般的な誤解

C++は複雑な言語であり、 しかも、 進化しつつある言語です。 その標準定義 (ISO C++標準) はつい最近完成したばかりです。 その結果、 C++コンパイラがときどきユーザを驚かすことがあります。 C++コンパイラの振る舞いが正しいような場合にすらそのようなことが起こります。 このセクションでは、 この種の質問が頻繁に持ち上がるようなところについて議論します。

静的メンバの宣言定義

あるクラスが静的なデータ・メンバを持つ場合には、 その静的メンバを宣言するだけでは十分ではなく、 その定義もしなければなりません。 例えば、

class Foo
{
  ...
  void method();
  static int bar;
};

この宣言は単に、 クラスFooFoo::barという名前のint型のメンバと Foo::methodという名前のメンバ関数を持つ、 ということをはっきりさせるだけです。 まだ、 methodbar両方をどこかほかのところで定義する必要があります。 ANSIのドラフト標準によれば、 1つの(ただ1つの)ソース・ファイルの中で初期化子を提供しなければなりません。 例えば、 以下のようになります。

int Foo::bar = 0;

他のC++コンパイラは、 この標準の振る舞いを正しく実装していないかもしれません。 その結果、 このようなコンパイラからg++に切り替えた人は、 以前は正しく動作していたように見えたプログラムが、 実際には標準に従っていないということに気が付くことになるかもしれません。 g++は、 定義されていない静的データ・メンバを見つけると、 それを未定義シンボルとして報告してきます。

予想よりも早く消滅する可能性のあるテンポラリ・オブジェクト

テンポラリ・オブジェクトの一部分に対するポインタや参照を使うことは危険です。 コンパイラが、 予想よりも早くそのオブジェクトを削除してしまい、 ポインタがゴミ(ガーベッジ)を指すようになってしまうことが当然ありえます。 この問題がもっともよく発生するのは、 stringクラスのようなクラスです。 特に、 char *型やconst char *型への変換関数が定義されているクラスにおいて顕著です。 標準のstringクラスにおいてc_strメンバ関数を呼び出すことが要求されるのは、 1つにはこのためです。 何らかの内部構造に対するポインタを返すクラスはいずれも、 潜在的にこのような問題を持つ可能性があります。

例えば、 あるプログラムが、 stringオブジェクトを返す関数strfuncchar型へのポインタを操作する別の関数charfuncを使うものとしましょう。

string strfunc ();
void charfunc (const char *);

void 
f ()
{
  const char *p = strfunc().c_str();
  ...
  charfunc (p);
  ...
  charfunc (p);
}

このような状況において、 繰り返しc_strを呼び出すのではなく、 c_strメンバ関数の返したC文字列へのポインタを待避しておいて、 そのポインタを繰り返し使うのは道理に合ったことのように見えるかもしれません。 しかし、 strfuncの呼び出しによって作成されたテンポラリstringオブジェクトはpの初期化の後に破棄され、 その時点で、 pは解放されたメモリ域を指すことになります。

ほかのコンパイラでは、 このようなコードもうまく動くことがあるかもしれません。 特に、 通常の局所変数とともにテンポラリ・オブジェクトを削除する、 時代遅れのcfrontベースのコンパイラであればそうでしょう。 しかし、 GNU C++の振る舞いは標準に適合しています。 テンポラリ・オブジェクトが遅いタイミングで削除されるということに依存するプログラムには、 移植性がありません。

このようなコードを安全に記述する方法は、 テンポラリ・オブジェクトに名前を付けることです。 こうすれば、 テンポラリ・オブジェクトはその名前のスコープの終端に達するまで存在し続けます。 例えば、 以下のようにします。

string& tmp = strfunc ();
charfunc (tmp.c_str ());

仮想基底クラスの暗黙のコピー代入

ある基底クラスが仮想基底クラスである場合、 個々のオブジェクトは、 その基底クラスのサブオブジェクトを1つだけ持ちます。 また、 生成関数と消去関数はただ1回だけ、 継承階層の最下位にあるクラスから実行されます。 しかし、 このようなオブジェクトも、 代入された場合の振る舞いは規定されていません。 例えば、 以下の例を考えてみましょう。

struct Base{
  char *name;
  Base(char *n) : name(strdup(n)){}
  Base& operator= (const Base& other){
   free (name);
   name = strdup (other.name);
  }
};

struct A:virtual Base{
  int val;
  A():Base("A"){}
};

struct B:virtual Base{
  int bval;
  B():Base("B"){}
};

struct Derived:public A, public B{
  Derived():Base("Derived"){}
};

void func(Derived &d1, Derived &d2)
{
  d1 = d2;
}

C++標準の規定によれば、 Derivedオブジェクトを生成またはコピー生成する際に、 `Base::Base'はただ1回だけ実行されます。 (この例における`func'のように) Derivedオブジェクトのコピー代入(copy-assignment)が暗黙のうちに実行されるときに、 `Base::operator='が1回以上呼び出されるかどうかについての規定はありません。

g++は、 コピー代入に関して「直観的」アルゴリズムを実装しています。 すなわち、 直接の基底クラスをすべて代入して、 その後に、 すべてのメンバを代入します。 このアルゴリズムでは、 仮想基底クラスのサブオブジェクトに何回も出くわす可能性があります。 上記の例では、 コピー処理は、 `val'、 (strdupによる)`name'`bval'、 再び`name'の順番で進行します。

アプリケーション・コードがコピー代入に依存するのであれば、 ユーザ定義のコピー代入演算子を定義することによって、 不確実性を取り除くことができます。 アプリケーションは、 ユーザ定義のコピー代入演算子を定義することによって、 仮想基底クラスのサブオブジェクトの代入の有無とその方法を定義することができます。

protoize の使用に関する警告

変換プログラムprotoizeunprotoizeは、 再修正をしないと正しく機能しなくなるような変更を ソース・ファイルに対して加えてしまうことがときどきあります。

変更したくない点

このセクションでは、 人々から頻繁に変更するよう要求されるにもかかわらず、 変更していない点を一覧にして示します。 これらの変更要求を採用しないのは、 そのような変更を加えないGCCの方が良いと考えられるからです。

警告メッセージとエラー・メッセージ

GNUコンパイラは、 2つの種類の診断を出力することができます。 エラーと警告です。 それぞれ異なる目的があります。

警告は、 プログラムが本当に意図されたとおりのことを行っているかどうかチェックして確認するべき危険な箇所を示しているかもしれません。 あるいは、 古くなってしまった機能を使っている箇所や、 GNU CやGNU C++の標準的ではない機能を使っている箇所を示しているかもしれません。 多くの警告は、 ユーザが`-W'オプションのどれかを使ってその警告を明示的に要求した場合にのみ出力されます (例えば、 `-Wall'は様々な有用な警告を要求するオプションです)。

GCCは常に、 可能であればプログラムをコンパイルしようと試みます。 プログラムの意味するところが明確であれば、 (例えば) 単に標準に正しく準拠していないという理由だけで、 いわれもなくプログラムを拒否するようなことはしません。 しかし、 CやC++の標準によってある特定の拡張が明示的に禁止されている場合があり、 そのような場合には、 標準に準拠するコンパイラは診断メッセージを出力しなければなりません`-pedantic'オプションは、 このような場合に警告を出力するようGCCに通知するものです。 一方、 `-pedantic-errors'はこのような場合に警告ではなくエラーを出力するよう指示します。 このことは、 ANSIに準拠していないすべての構文に対して警告やエラーが出力されることを意味しているわけではありません。

これらの点に関する詳細、 および、 関連するコマンドライン・オプションの詳細については、 警告を要求もしくは抑制するオプションを参照してください。


[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]