このセクションでは、 GNU CC のユーザに影響を及ぼす既知の問題について説明します。 これらの問題のほとんどは、 それ自体は GNU CC のバグではありません。 もしバグであれば、 私たちはそれを修正したでしょう。 ただし、 これらの問題がユーザに対して及ぼす結果は、 バグがもたらす結果と同じようなものかもしれません。
これらの問題のいくつかは、 他のソフトウェアのバグによるものです。 また、 それを追加するにはあまりにも労力のかかるような機能の欠如が問題であることもあります。 さらに、 何が最善であるかについて人々の意見が一致しない領域が問題となることもあります。
fixincludes
スクリプトとオートマウンタとの相性は良くありません。
システム・ヘッダ・ファイルの存在するディレクトリがオートマウントされていると、
そのディレクトリは、
fixincludes
の実行中にアンマウントされてしまう傾向があります。
これはオートマウンタのバグであるように思われます。
これをうまく回避する方法を、
私たちは知りません。
fixproto
スクリプトは時々、
jmp_buf
型が定義されるよりも前に、
jmp_buf
型を参照する sigsetjmp
関数や siglongjmp
関数のプロトタイプを追加します。
これを回避するためには、
問題を引き起こすファイルを編集して、
プロトタイプの前に typedef を記述してください。
以下は、 GNU CC をインストールする際に出てくる問題 (および、 実際には何か問題があることを意味しているわけではない、 見せかけだけの問題) の一覧です。
CC
のような特定の環境変数を定義すると、
make
の動作の妨げになることがあります。
fixincludes
の実行において問題が発生するかもしれません。
これらの問題は、
結果的には、
`sys/types.h' の中にある size_t
の宣言の修正失敗につながります。
size_t
が有符号の型であり、
型のミスマッチが発生しているのであれば、
このことが原因である可能性があります。
解決策は、
GNU CC を構築するのにこのようなディレクトリを使わないことです。
gcc
ドライバ・プログラムは、
as
と ld
を見つけるためにいくつかのディレクトリを探索していました。
例えば、
`/usr/local/lib/gcc-' で始まる名前のファイルを探していました。
GNU CC バージョン 2 では、
これらは、
ディレクトリ `/usr/local/lib/gcc-lib/target/version' において探されます。
したがって、
例えば gas
や GNU ld
のような、
システムのデフォルトではない as
や ld
を使うためには、
それらを上記のディレクトリに置かなければなりません
(あるいは、
上記のディレクトリからリンクを設定しなければなりません)。
make
がこれを無視することがあります。
多くの場合ファイルが見つからないことが原因であるこれらの異常終了は、
予期されたものであり、
無視しても安全です。
make
がコンパイラの一部を再コンパイルすることがあります。
このようなケースで、
調査の結果 make
のバグであることが判明したものが1つあります。
この問題は無視するか、
あるいは、
GNU make に切り替えてください。
enquire
のリンク時にエラーが発生することがあるかもしれません。
この問題を修正するには、
purify がインストールするファイル real-ld
を削除して、
GNU CC がそれを使おうと試みることのないようにしてください。
__GNU_LIBRARY__
条件式を `#if 1' に変更してください。
enquire
がハングするために、
コンパイラの構築が永久に完了しないことがあります。
カーネルに対してマザーボードが浮動小数点例外を誤って報告するのです。
enquire
を実行するコマンドをパッチによりはずすことによって、
`float.h' を除く GNU CC をインストールすることができます。
代わりのマザーボードを手に入れることで、
この問題を本当に修正してしまうこともできるかもしれません。
この問題は、
Micronics マザーボードの Revision E で発生し、
Revision F で修正されました。
また、
MYLEX MXA-33 マザーボードでも発生しました。
もしこの問題に出会ったら、
コンパイルをしている間、
ソケットから FPU をはずすことを検討するとよいかもしれません。
SCO UNIX を実行している場合には、
別の方法として、
リブートして FPU を強制的に無視させることもできます。
これをするには、
`hd(40)unix auto ignorefpu' を実行します。
ln /etc/emulator.rel1 /etc/emulatorをスーパー・ユーザとして実行し、 そのあとでシステムをリブートしてください (デフォルトのエミュレータ・ファイルは、 `emulator.dflt' という名前で残ります)。 SCO のシステムでこのような問題がある場合には、 `/etc/emulator.att' を使ってみてください。 このような問題を持つ別のシステムに Esix があります。 Esix が正しく動作する代替エミュレータを持っているかどうかを私たちは知りません。 NetBSD 0.8 では、 これと類似の問題が、 以下のようなエラー・メッセージが出力されることで表面化します。
enquire.c: In function `fprop': enquire.c:2328: floating overflow
genflags
や genoutput
というプログラムがクラッシュするのに気がつくかもしれません。
これは sh
のバグのためであると言われています。
手作業で genflags
や genoutput
を実行してから make
を再試行することによって、
おそらくこの問題を回避することができるでしょう。
pkginfo
コマンドを使ってください。
また、
ある任意選択パッケージを追加するには、
pkgadd
を使ってください。
さらに詳しい情報については、
Solaris のドキュメントを参照してください。
Solaris の 2.0 と 2.1 では、
GNU CC は6個のパッケージを必要とします。
それらは、
`SUNWarc'、
`SUNWbtool'、
`SUNWesu'、
`SUNWhea'、
`SUNWlibm'、
`SUNWtoo' です。
Solaris 2.2 では、
GNU CC はさらに7個目のパッケージとして `SUNWsprot' を必要とします。
PATH
から `/usr/ucb' を取り除くことです。
add.d
のような浮動小数点命令を埋め込むと、
アセンブラが文句を言ってくるでしょう。
fixincludes
によって修正されない限り、
GNU CC との組み合わせでは使えません。
これは、
GNU CC の構築において問題を引き起こします。
GNU CC がインストールされてしまえば、
それ以上問題は発生しません。
この問題を回避するには、
ステージ 1 のコンパイラを作成する際に、
以下のオプションを make に対して指定してください。
GCC_FOR_TARGET="./xgcc -B./ -I./include"また、 ステージ 2 と ステージ 3 を作成する際には、 以下のオプションを指定してください。
CFLAGS="-g -I./include"
alloca
を使うコードをリンクする際に、
いくつかのバージョンの MIPS リンカは診断(assertion)失敗を通知してきます。
これはリンカのバグであり、
将来のリビジョンで修正されるべきものです。
この問題を防ぐために、
ユーザが明示的に `-shared' や `-call_shared' を渡さない限り、
GNU CC はリンカに対して `-non_shared' を渡します。
ld fatal: failed to write symbol name something in strings table for file whateverこれは、 ディスクが満杯になってしまったか、 ULIMIT の制限のためにファイルが必要なサイズにまで大きくなることができないということを示唆しています。 この問題は、 カーネル・パラメータ
MAXUMEM
が小さすぎるために引き起こされることもありえます。
この場合には、
カーネルを再構築してその値をずっと大きくしなければなりません。
デフォルトの値は 1024 とのことです。
この値を 32768 にすれば正しく動作すると言われています。
これより小さい値でも正しく動作するかもしれません。
/usr/local/lib/bison.simple: In function `yyparse': /usr/local/lib/bison.simple:625: virtual memory exhaustedのようなエラーが出るのであれば、 これもまた、 ディスク・スペース、 ULIMIT、
MAXUMEM
に問題のあることを示唆しています。
MAXUMEM = 4096
_floatdisf cc1: warning: `-g' option not supported on this version of GCC cc1: warning: `-g1' option not supported on this version of GCC ./xgcc: Internal compiler error: program as got fatal signal 11
altdorf.ai.mit.edu
から `archive/cph/hpux-8.0-assembler' という名前のファイルを anonymous ftp することによって、
パッチをあてたアセンブラが入手できます。
HP 社とソフトウェア・サポート契約を締結していれば、
直接 HP 社からこのパッチを入手することもできます。
このパッチについては、
以下の引用に示すような説明がされています。
このパッチは、 PHCO_4484 としても知られています。これは、 浮動小数点定数があるとアセンブラがアボートするという問題 SR#1653-010439 を解消するためにパッチをあてたアセンブラです。
このバグは実際にはアセンブラの中にあるのではなく、 関数 "cvtnum(3c)" の共用ライブラリ版にあります。 "cvtnum(3c)" のバグは SR#4701-078451 です。 添付されているアセンブラでは、 "cvtnum(3c)" のアーカイブ・ライブラリ版を使っているので、 このバグが現れることはありません。 (12)
fixproto
はシステムのシェルのバグを引き起こします。
バージョン 8.07 以降のバージョンではこのようなことは起こりません。
この問題に出会ったら、
オペレーティング・システムをアップグレードするか、
fixproto
を実行するのに BASH(GNU シェル)を使ってください。
muldi3
をコンパイルする際に GNU CC がクラッシュするのであれば、
この問題が存在するということです。
バージョン 1 の GNU CC を入手、
インストールして、
それを使ってバージョン 2 の GNU CC をコンパイルすれば、
うまくいく可能性があります。
Pyramid C コンパイラのバグは、
バージョン 1 の GNU CC には影響を及ぼさないようです。
va_arg
の再定義に関する警告やエラーが出るでしょう。
この場合には、
ほとんどのプログラムをライブラリ `iclib.a' とリンクする必要があります。
また、
`stdio.h' を以下のように修正しなければなりません。
#if defined(__i860__) && !defined(_VA_LIST) #include <va_list.h>という2行の前に、
#if __PGC__という1行を追加し、 さらに、
extern int vprintf(const char *, va_list ); extern int vsprintf(char *, const char *, va_list ); #endifという3行の後ろに、
#endif /* __PGC__ */という1行を追加します。 これらの問題は、 バージョン 1.1 のオペレーティング・システムには存在しません。
./fixproto: sh internal 1K buffer overflowこれを修正するには、 fixproto スクリプトの最初の行が以下のようになるよう変更します。
#!/bin/ksh
いくつかの理由により、 特定のマシン上においてクロス・コンパイル処理を行う際に問題に直面することがあるかもしれません。
REAL_VALUE_TYPE
のようなマクロを定義することによって、
これらの問題を克服することが可能です。
しかし、
これを行うのは、
個々のターゲット・マシンについて相当な量の作業となります。
@xref{Cross-compilation}。
このセクションでは、 GNU C や GNU C++ を他のコンパイラと一緒に使う場合や、 特定のシステム上のアセンブラ、 リンカ、 ライブラリ、 デバッガと一緒に使う場合に直面する様々な問題を一覧にして示します。
fixincludes
によって修正されない限り、
GNU CC との組み合わせでは正しく機能しません。
これによって修正されたヘッダ・ファイルは、
新規ディレクトリに移されます。
GNU CC は、
`/usr/include' よりも前にこのディレクトリを探索します。
`-I/usr/include' を使うと、
修正されたヘッダ・ファイルよりも前に `/usr/include' を探索するよう GNU CC に対して通知することになります。
結果的に、
未修正のヘッダ・ファイルが使われることになります。
`-I/usr/include' ではなく、
(C のプログラムをコンパイルする際には)
以下のオプションを使うべきです。
-I/usr/local/lib/gcc-lib/target/version/include -I/usr/includeC++ のプログラムについても、 標準 C サブルーチンに対する C++ インターフェイスを定義する特別なディレクトリを GNU CC は使います。 このディレクトリもまた、 それが優先されるよう、 他の標準インクルード・ディレクトリよりも前に探索されることになっています。 C++ プログラムをコンパイルする際にインクルード・ディレクトリを明示的に指定するのであれば、 まず以下のオプションを最初に指定して、 そのあとで上記の2つのオプションを指定してください。
-I/usr/local/lib/g++-include
double
型の値を 8 バイト境界に境界整列します。
また、
すべての double
型の値が 8 バイト境界に境界整列されているものと期待しています。
Sun 社のコンパイラは通常、
double
型の値を 8 バイト単位に境界整列しますが、
1つだけ例外があります。
double
型の関数引数は境界整列されない可能性があります。
この結果、
Sun CC によりコンパイルされたある関数が double
型の引数のアドレスを取り、
この double *
型のポインタを GNU CC によりコンパイルされた関数に渡すと、
そのポインタを使ってそのポインタにより指し示される実体にアクセスしようとしたときに致命的なシグナルの発生する可能性があります。
この問題を解決するための1つの方法は、
プログラム全体を GNU CC を使ってコンパイルするということです。
別の解決策として、
Sun CC によりコンパイルされる関数を、
引数を局所変数にコピーするように変更するという方法もあります。
局所変数は常に正しく境界整列されます。
第3の解決策として、
ポインタを使う関数において直接 `*' を使うのではなく、
下に示す関数 access_double
を経由して実体にアクセスするよう変更するという方法もあります。
inline double access_double (double *unaligned_ptr) { union d2i { double d; int i[2]; }; union d2i *p = (union d2i *) unaligned_ptr; union d2i u; u.i[0] = p->i[0]; u.i[1] = p->i[1]; return u.d; }ポインタを使って値を格納する場合も、 同じ共用体を使って同様に行うことができます。
malloc
関数は、
4 バイト境界にしか境界整列されていないメモリを割り当てることがあります。
Sparc 上の GNU CC は double 型が 8 バイト境界に境界整列されることを想定していますので、
`libmalloc.a' ライブラリにより割り当てられたメモリ内に double 型の値が存在すると、
致命的なシグナルがその結果として発生する可能性があります。
解決策は、
`libmalloc.a' ライブラリを使わないことです。
その代わりとして、
`libc.a' の malloc
関数やこれに関連する関数を使ってください。
こちらの関数群にはこのような問題はありません。
_dlclose
、
_dlsym
、
_dlopen
というシンボルが未定義になるようであれば、
MIT 版 X Window の `mit/util/misc/dlsym.c' というファイルを使ってコンパイルとリンクを行ってください。
cc
が GNU CC を正しくコンパイルしません。
今のところ原因は分かっていませんが、
9.01 よりも前のバージョンの HP-UX 上でコンパイルされた GNU CC は、
HP-UX 9.01 上でも正しく動作しますし、
9.01 上で GNU CC 自身をコンパイルすることができます。
alloca
や可変サイズの配列を使う関数に対して正しく動作しなくなります。
原因は、
このような関数に対して GNU CC が HP-UX のアンワインド記述子(unwind descriptor)を生成しないということにあります。
これを生成することは不可能ですらあるかもしれません。
(warning) Use of GR3 when frame >= 8192 may cause conflict.これらの警告は無害ですので、 無視しても安全です。
as -u < /dev/nullこのコマンドが正常に終了すれば、 修正版のアセンブラは既にインストールされています。 アセンブラが、 "-u" は未知のフラグであると文句を言ってくるようであれば、 修正版を要求する必要があります。
foo
は未定義シンボルであるとリンカが報告してきます。
extern int foo; ... foo ... static int foo;このような動作は、 他のほとんどのシステム上での動作とは異なりますが、 バグではありません。
extern
変数を static
として再定義した場合の動作は、
ANSI C では未定義となっているからです。
size_t
の typedef が2つあるためにエラーになります。
size_t
の定義のまわりに以下の行を追加することによって、
`sys/types.h' を変更しなければなりません。
#ifndef _SIZE_T #define _SIZE_T typedefはここに置く #endif
-fcall-saved-r2 -fcall-saved-r3 -fcall-saved-r4 -fcall-saved-r5
-L/usr/local/lib/gcc-lib/we32k-att-sysv/2.8.1 -lgcc -lc_s最初のオプションは、 `-lgcc' オプションにより指定されたライブラリ `libgcc.a' を見つけるべき場所を指定しています。 GNU CC は、
cc
と同様、
ld
を起動することによってリンク処理を行います。
ld
を起動するのに使ったコンパイル処理プログラムの種類によってリンク処理に違いの出る理由がありません。
誰かがこの問題の原因を追求すれば、
おそらく簡単に修正することができるでしょう。
ecvt
、
fcvt
、
gcvt
のバグによるものです。
これらの関数は、
正当な浮動小数点数が与えられたにもかかわらず、
`NaN'(13) を出力することがあります。
プログラムによっては、 コンパイルに問題のあることがあります。
#ifdef __STDC__ #define NeedFunctionPrototypes 0 #endif
-traditional -Dvolatile=__volatile__ -I/usr/include/sun -I/usr/ucbinclude -fpcc-struct-returnこのほとんどは、 GCC 2.4.5 以降のバージョンでは必要ありません。 `config.sh' の中で、
ccflags
を
(元のオプションの中の `-traditional' により暗黙のうちに指定される)
`-fwritable-strings' に、
cppflags
を空にそれぞれ設定した後に、
`./doSH; make depend; make' を実行することで、
正しく動作する Perl を作成することができます。
MALLOC=/usr/local/lib/libgmalloc.aあるいは、 Emacs 19 の `gmalloc.c' をコンパイル済みであれば、 そのオブジェクト・ファイルを `gmalloc.o' にコピーして、 GNU CC を再リンクする際に以下のオプションを使ってください。
MALLOC=gmalloc.o
GNU C と既存の (ANSI に対応していない) ほとんどの C との間には、 注目すべき非互換性がいくつかあります。 `-traditional' オプションを使うと、 他の C コンパイラと似た振る舞いをするよう GNU CC に通知がなされ、 これらの非互換性の多くが取り除かれます。 しかし、 すべての非互換性が取り除かれるわけではありません。
mktemp
を呼び出すことができません。
関数 mktemp
は常に、
引数が指し示す文字列を書き換えます。
もう1つの結果として、
いくつかのシステムでは、
書式文字列や入力として文字列定数が渡された場合に sscanf
が正しく動作しません。
これは、
sscanf
が誤って文字列定数の中に書き込みを行おうとするからです。
fscanf
や scanf
についても同様のことが言えます。
これらの問題に対する最善の解決策は、
プログラムを変更して、
このような目的では文字列定数を使わず、
初期化文字列を指定した char
の配列変数を使うようにすることです。
これができない場合は、
`-fwritable-strings' フラグを使うことができます。
このフラグは、
ほとんどの C コンパイラと同様の方法で文字列定数を取り扱うよう、
GNU CC に対して指示するものです。
他のオプションでは、
`-traditional' もまたこのような効果を持っています。
-2147483648
は正の数です。
これは、
2147483648 が int
型に収まらないため、
(ANSI C のルールにしたがい)
そのデータ型が unsigned long int
となるからです。
この値の符号を負に変えても、
再び 2147483648 という値になります。
#define foo(a) "a"この場合 GNU CC は、 引数 a が何であるかにかかわらず
"a"
を出力します。
`-traditional' オプションは、
このような場合に古い
(非 ANSI の)
方法で処理するよう GNU CC に指示します。
setjmp
と longjmp
を使うと、
有効であり続けることが保証される唯一の自動変数は、
volatile
宣言されたものだけです。
これは、
自動レジスタ割り当ての結果です。
以下の関数を考えてみてください。
jmp_buf j; foo () { int a, b; a = fun1 (); if (setjmp (j)) return a; a = fun2 (); /*ここで、longjmp (j)
may occur infun3
. */ return a + fun3 (); }
longjmp
が呼び出された時に、
a
はその最初の値に復元されることもありますし、
されないこともあります。
a
がレジスタの中に割り当てられていれば、
その最初の値が復元されます。
これ以外の場合は、
最後に格納されていた値が保持されます。
`-O' オプションとともに `-W' オプションを使うと、
そのような問題の発生する可能性があると GNU CC がみなした場合には、
警告が出力されます。
`-traditional' オプションは、
setjmp
を呼び出す関数の中の変数を、
レジスタの中にではなく、
デフォルトでスタックに割り当てるよう GNU C に指示します。
これにより、
伝統的な C コンパイラのような振る舞いをするようになります。
foobar ( #define luser hack)ANSI C はこのような構文を認めていません。 `-traditional' が使われた場合にこれをサポートするというのは道理には合っているでしょうが、 それを実装するのはあまりにも大変な作業です。
extern
宣言は、
たとえそれがあるブロックの中で行われたとしても、
ファイルの残りの部分全体に影響を与えます。
`-traditional' オプションは、
伝統的なコンパイラのように、
すべての extern
宣言を広域宣言として扱うよう GNU C に指示します。
long
等を typedef された名前と結合させることができます。
typedef int foo; typedef long foo bar;ANSI C では、 このようなことは許されません。
long
やほかの型修飾子は、
明示的に int
を必要とします。
この基準は C のコードとしてではなく Bison の文法規則として表現されているため、
`-traditional' フラグを使ってもこれを変更することはできません。
#if 0 You can't expect this to work. #endifこのような問題に対する最善の解決策は、 このテキストを `/*...*/' によって境界の定められた本当の C のコメントの中に入れてしまうことです。 また、 `-traditional' を使うと、 このようなエラー・メッセージは出力されなくなります。
time
を実際には宣言していませんでした。
したがって、
ユーザ・プログラムがこの関数の戻り値の型をどう宣言しようが、
問題はありませんでした。
しかし、
ANSI C ヘッダを持つシステムでは、
time
は time_t
型の戻り値を持つものとして宣言されています。
time_t
型が long
型と同じではないのであれば、
`long time ();' という宣言は誤りです。
解決策は、
time
の戻り値の型として time_t
を使うようユーザ・プログラムを変更することです。
float
を戻り値とする関数を PCC でコンパイルすると、
PCC はそれを double に変換します。
GNU CC は実際に float
を返します。
PCC との互換性が気になるのであれば、
double
を返すよう関数を宣言するべきでしょう。
そして、
そのようにしたことの意図を明記しておくのがよいでしょう。
STRUCT_VALUE
と STRUCT_INCOMING_VALUE
が、
このアドレスをどこに入れて渡すべきかを GNU CC に対して通知します。
これに対して、
ほとんどのターゲット・マシン上の PCC が構造体や共用体を返す方法は、
構造体や共用体のサイズにかかわりなくそのデータを静的な記憶領域にコピーして、
その記憶域のアドレスをあたかもそれがポインタ値であるかのように返す、
というものです。
呼び出し側は、
そのメモリ領域から、
値が要求されている領域へデータをコピーしなければなりません。
この方法は速度も比較的遅く、
再入可能でもないので、
GNU CC はこの方法を使っていません。
いくつかの比較的新しいマシン上では、
構造体や共用体を戻り値として返すすべての場合において、
PCC は再入可能な規約を使っています。
このようなマシンのほとんどにおいて GNU CC は、
構造体や共用体をメモリ上に置いて返す場合にはこれと互換性のある規約を使用していますが、
サイズの小さい構造体や共用体はやはりレジスタに入れて返しています。
オプション `-fpcc-struct-return' を使うことによって、
構造体や共用体を戻り値として返すすべての場合において互換性のある規約を使うよう GNU CC に通知することができます。
GNU CC は、 いくつかのシステム・ヘッダ・ファイルを訂正したものをインストールしておくことを必要とします。 これは、 ほとんどのターゲット・システムのヘッダ・ファイルには、 未修正のままでは GNU CC と組み合わせて使うことができないものがあるからです。 バグを持つものもありますし、 ANSI C と非互換のものもあります。 また、 他のコンパイラの特殊な特徴に依存しているものもあります。
GNU CC をインストールすると、
fixincludes
と呼ばれるプログラム
(あるいは、
あるターゲット・マシンでは、
例えば fixinc.svr4
のような代わりのプログラム)
を実行することにより、
修正されたヘッダ・ファイルが自動的に作成され、
インストールされます。
通常ユーザは、
このことに注意を払う必要はありません。
しかし、
このプログラムが自動的には正しいことをしてくれないケースもあります。
fixincludes
スクリプトの実行が失敗するからです。
このことは、
システム・ヘッダ・ファイルのバグを原因とする問題にユーザが直面するであろうことを意味しています。
これが GNU CC の責任ではないということは何の慰めにもならないかもしれませんが、
これについて私たちにできることは何もないということです。
GNU CC は、 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 *
を戻すのですから、
これで正しくなるわけではありません。
標準に準拠したライブラリが必要であれば、
それを自分で探す必要があります。
GNU CC は標準に準拠したライブラリを提供していません。
(glibc
と呼ばれる)GNU C ライブラリは、
いくつかのオペレーティング・システムに移植されており、
ANSI/ISO、
POSIX、
BSD、
SystemV との互換性を提供しています。
あるいは、
より新しいライブラリが利用可能になっているかどうかを、
オペレーティング・システムのベンダに問い合わせてみることもできるでしょう。
以下の問題が存在することは残念ではありますが、 実際にこれを回避するための方法を私たちは1つも知りません。
int foo (struct mumble *); struct mumble { ... }; int foo (struct mumble *x) { ... }このソース・コードは本当に誤りです。 何故なら、 プロトタイプ中の
struct mumble
のスコープは、
この struct mumble
を含む引数リストの中に限定されているからです。
この struct mumble
は、
その直後にファイル・スコープをもって定義されている struct mumble
を参照しません。
これらは、
異なるスコープの中で類似の名前を持つ、
互いに無関係な2つの型です。
しかし、
foo
の定義においては、
ファイル・スコープを持つ型を受け継ぐことができますから、
それが使われます。
このために定義とプロトタイプが一致しないので、
エラーとなるのです。
このような振る舞いはばかげていると思われるかもしれませんが、
ANSI 標準がこれを規定しているのです。
struct mumble
の定義をプロトタイプよりも上に移動すれば、
このソース・コードは正しく機能するようになりますが、
これは極めて簡単なことです。
上記のような例においてエラーになることを回避するためだけの目的で、
GNU CC を ANSI C と互換性のないものにする値打ちはありません。
include
を削除してから、
再度 `make install' を実行します。
double
型に収まるには、
少々ビット数が多いからです。
コンパイルされたコードは、
メモリと浮動小数点レジスタとの間で都合のいいように値を移動します。
値をメモリへ移動すると、
その値は切り捨てられます。
この問題は、
`-ffloat-store' オプションを使うことにより部分的に回避することができます
(see section 最適化を制御するオプション)。
C++ は複雑な言語であり、 しかも、 進化しつつある言語です。 その標準定義 (ANSI C++ ドラフト標準) もまた進化しつつあります。 その結果、 C++ コンパイラが時々ユーザを驚かすことがあります。 C++ コンパイラの振る舞いが正しいような場合にすらそのようなことが起こります。 このセクションでは、 この種の質問が頻繁に持ち上がるようなところについて議論します。
あるクラスが静的なデータ・メンバを持つ場合には、 その静的メンバを宣言するだけでは十分ではなく、 その定義もしなければなりません。 例えば、
class Foo { ... void method(); static int bar; };
この宣言は単に、
クラス Foo
が Foo::bar
という名前の int
型のメンバと Foo::method
という名前のメンバ関数を持つ、
ということを確かなものにするだけです。
まだ method
と bar
の両方をどこかほかのところで定義する必要があります。
ANSI のドラフト標準によれば、
1つの(ただ1つの)ソース・ファイルの中で初期化子を提供しなければなりません。
例えば、
以下のようになります。
int Foo::bar = 0;
他の C++ コンパイラは、
標準のこの振る舞いを正しく実装していないかもしれません。
その結果、
このようなコンパイラから g++
に切り替えた人は、
以前は正しく動作していたように見えたプログラムが、
実際には標準に従っていないということに気がつくことになるかもしれません。
g++
は、
定義されていない静的データ・メンバを見つけると、
それを未定義シンボルとして報告してきます。
テンポラリ・オブジェクトの一部分に対するポインタや参照を使うことは危険です。
予想よりも早く、
コンパイラがそのオブジェクトを削除してしまい、
ポインタがゴミ(ガーベッジ)を指すことが当然ありえます。
この問題がもっともよく発生するのは、
libg++ の String
クラスのように、
char *
型や const char *
型への変換関数が定義されているクラスにおいてです。
何らかの内部構造に対するポインタを返すクラスはいずれも、
潜在的にこのような問題を持つ可能性があります。
例えば、
あるプログラムが、
String
オブジェクトを返す関数 strfunc
と char
型へのポインタを操作する別の関数 charfunc
を使うものとしましょう。
String strfunc (); void charfunc (const char *);
このような状況において、
クラス String
が char
型に対するポインタへの明示的な変換関数を持つことが分かっていますから、
それにもとづいて `charfunc (strfunc ());' と書くのは自然なことに思えるかもしれません。
しかし、
実際の動作は `charfunc (strfunc ().convert ());' に類似したものとなります。
ここで convert
メソッドは、
通常キャストによって行われるデータ変換と同じことを行う関数です。
String
テンポラリ・オブジェクトが最後に使われるのは変換関数の呼び出しにおいてですから、
charfunc
が実際に呼び出される前に、
コンパイラはこのオブジェクトを削除してしまうかもしれません。
コンパイラには、
String
を削除してしまうとポインタが無効になってしまうことを知る術がありません。
こうしてポインタはゴミ(ガーベッジ)を指すことになり、
charfunc
は、
呼び出された時に不当な引数を受け取ることになります。
ほかのコンパイラでは、 このようなコードもうまく動くことがあるかもしれません。 特に、 テンポラリ・オブジェクトを削除するタイミングが比較的遅いコンパイラであればそうでしょう。 しかし、 GNU C++ の振る舞いも標準に適合しているのです。 テンポラリ・オブジェクトが遅いタイミングで削除されるということに依存するプログラムがあれば、 そのプログラムには移植性がありません。
もしこれが驚くべきことであると思われるのであれば、 ANSI C++ 委員会ではテンポラリ・オブジェクトのライフタイムの問題に関して今でも議論を続けているということを知っておくべきでしょう。
すくなくとも現在のところは、 このようなコードを安全に書く方法は、 テンポラリ・オブジェクトに名前をつけることです。 こうすれば、 テンポラリ・オブジェクトはその名前のスコープの終端に達するまで存在し続けます。 例えば、 以下のようにします。
String& tmp = strfunc (); charfunc (tmp);
protoize
の使用に関する警告
変換プログラム protoize
と unprotoize
は時々ソース・ファイルに対して、
再修正をしないと正しく機能しなくなるような変更を加えてしまうことがあります。
protoize
は、
型が定義されるよりも前に、
その型の名前への参照やその型のタグへの参照を挿入することがあります。
また、
型が定義されていないファイルの中において、
その型の名前への参照やその型のタグへの参照を挿入することもあります。
このようなことが起こった場合でも、
コンパイラのエラー・メッセージによってどこに新しく挿入された参照があるかが分かるはずですので、
そのファイルを手作業で修正するのは複雑なことではありません。
protoize
には理解することのできない C の構文があります。
例えば、
関数ポインタ変数を宣言する際の引数の型を決定することはできません。
これは手作業で行わなければなりません。
protoize
は、
そのような変数を見つけるたびに `???' という文字列を含むコメントを挿入します。
したがって、
このような変数はすべて、
この文字列を検索することによって見つけることができます。
ANSI C では、
関数ポインタ型において引数の型を宣言することは必須ではありません。
unprotoize
を使うと、
簡単にバグが入り込んでしまうことがあります。
プロトタイプの作用により引数の型変換が行われることをあてにしているプログラムでは、
プロトタイプがなくなればこのような変換は行われなくなってしまいます。
unprotoize
を使うことが安全であると確信できる1つのケースは、
protoize
により生成されたプロトタイプを除去する場合です。
プロトタイプが生成される以前からプログラムが正しく動作していたのであれば、
プロトタイプを除去した後も正しく動作するでしょう。
この問題が発生する可能性のある箇所はすべて、
`-Wconversion' オプションを使ってプログラムをコンパイルすることによって見つけることができます。
これにより、
引数が変換されるところでは常に警告が表示されます。
protoize
は知ることができません。
このようなことが起こると、
protoize
はそのような関数に関しては何も変更しません。
protoize
は、
このようなケースに相当する部分を検出して、
そのことに関する警告を出力しようと試みます。
一般的には、
すべての関数が変換されるまで、
コンパイル処理に毎回異なる組み合わせの `-D' オプションを指定して段階的に protoize
を使うことによって、
この問題を回避することができます。
しかしながら、
すべての関数が正しく変換されたことを自動的に検証する方法はありません。
unprotoize
は混乱することがあります。
このような形式パラメータ名は選択されないようお勧めします。
このセクションでは、 人々から頻繁に変更するよう要求されるにもかかわらず、 変更していない点を一覧にして示します。 これらの変更要求を採用しないのは、 そのような変更を加えない GNU CC の方が良いと考えられるからです。
void
へのキャストを追加することによってプログラムの中を取り散らかすことには、
何の意味もありません。
int
として宣言されたビットフィールドが有符号であるか無符号であるかは実装依存であるとしています。
このことは事実上、
2つの代替的な C の方言を作ることになります。
GNU C コンパイラは、
どちらの方言もサポートします。
有符号となる方言については `-fsigned-bitfields' を使って、
また、
無符号となる方言については `-funsigned-bitfields' を使って、
それぞれ指定することができます。
しかしこれだけでは、
デフォルトでどちらの方言を使うのかという問題は未解決のままです。
現在のところ、
デフォルトとして選択された方言ではビットフィールドを有符号にしています。
こうするのが最も簡単であるからです。
他のあらゆるコンテキストにおいて、
int
は signed int
と同一ですので、
ビットフィールドの中でもこの両者が同一であるのが最もすっきりしています。
いくつかのコンピュータ製造業者は、
ただのビットフィールドは無符号でなければならないと指定するアプリケーション・バイナリ・インターフェイス標準を発表しました。
しかし、
このような問題について ABI の中で触れるのは誤りです。
ただのビットフィールドをどう扱うかによって C の2つの方言が区別されるからです。
これら2つの C の方言は、
どのような種類のマシン上でも意味を持ちます。
ある特定のオブジェクト・ファイルが有符号のビットフィールドを使ってコンパイルされたか無符号のビットフィールドを使ってコンパイルされたかということは、
他のオブジェクト・ファイルには無関係なことです。
他のオブジェクト・ファイルが同一のデータ構造の中の同一のビットフィールドをアクセスする場合ですら、
無関係です。
ある与えられたプログラムは、
これら2つの方言のいずれか一方を使って書かれます。
このプログラムは、
適切な方言を使ってコンパイルされれば、
ほとんどすべてのマシン上で動作する見込みがあります。
逆に間違った方言を使ってコンパイルされてしまうと、
正しく動作することはほとんどありえないでしょう。
多くのユーザが GNU C コンパイラの真価を認めているのは、
それが異なるマシン間において同一の環境を提供してくれるからです。
もしこのコンパイラが、
特定のマシン上においてだけ、
ただのビットフィールドに対して異なる取り扱いをすることになれば、
こうしたユーザは不便を感じることでしょう。
ユーザは時々、
特定のマシン・タイプだけを対象にしたプログラムを書きます。
このような場合には、
そのマシン上の他のコンパイラがサポートしているのと同一の方言を GNU C コンパイラがデフォルトでサポートしていれば、
そのユーザにとっては有益でしょう。
しかし、
このようなアプリケーションはまれにしか存在しません。
しかも、
複数のタイプのマシン上で動作するプログラムを書くユーザにとっては、
このような種類の互換性があっても何の得にもならないでしょう。
これが、
GNU CC が現在も今後も、
すべてのタイプのマシン上においてただのビットフィールドを
(デフォルトでは)
同一の方法で取り扱う理由です。
すべてのマシン上においてビットフィールドのデフォルトを無符号にしようという議論があります。
例えばこれが普遍的なデファクト標準となることがあれば、
GNU CC がそれに追随するのも意味のあることです。
これは、
将来検討されるべきことです。
(もちろん、
移植性に強い関心を持つユーザは、
個々のビットフィールドにおいて、
それが有符号なのか無符号なのかを明示的に示すべきです。
このようにすれば、
どちらの C 方言においても同一の意味を持つプログラムを書くことができます)
__STDC__
を未定義とすること。
現在 GNU CC は、
`-traditional' が使われない限り __STDC__
を定義しています。
このことは実際に良い結果をもたらしています。
関数プロトタイプや ANSI のトークン連結のような、
ANSI C の特定の機能を使っても安全かどうかを確認するのに、
プログラマは通常 __STDC__
に対する条件式を使います。
`gcc' はそのままで ANSI C の機能をすべてサポートしていますので、
このような確認に対する正しい答えは「はい、安全です」です。
ユーザの中には、
ある特定のライブラリが利用できるかどうかをチェックするのに __STDC__
を使おうとする人もいます。
ANSI C プログラムの中でこのような使い方をするのは実際には正しくありません。
というのは、
ANSI C 標準によれば、
ANSI C に準拠する自立的な(freestanding)実装は、
たとえライブラリを提供していなくても __STDC__
を定義するべきだからです。
`gcc -ansi -pedantic' というように起動された場合の GNU CC は ANSI C に準拠する自立的な実装となるので、
ANSI C ライブラリが付属していなくても __STDC__
を定義することが要求されるのです。
時々、
ANSI C 標準に完全には準拠していないコンパイラにおいて __STDC__
を定義することは標準に違反している、
ということが言われます。
これは非論理的です。
ここでの標準とは、
`gcc -ansi' として起動された場合の GNU CC のように ANSI C をサポートしていると主張するコンパイラに対する標準であり、
単に `gcc' として起動された場合の GNU CC のようなその他のコンパイラに対する標準ではありません。
ANSI C で規定されていることが、
`-ansi' の指定されない単なる `gcc' の設計にも関連してくるのは、
実用主義的な理由からであって、
要件としてではありません。
GNU CC は通常 __STDC__
を 1 として定義し、
さらに、
`-ansi' オプションが指定されている場合には __STRICT_ANSI__
を定義します。
いくつかのホストでは、
システムのインクルード・ファイルがこれとは異なる規約を使っています。
そこでは、
__STDC__
は通常は 0 であり、
ユーザが C 標準への厳密な適合性を指定した場合には 1 となります。
GNU CC は、
システムのインクルード・ファイルを処理する際にはそのホストの規約に従いますが、
ユーザ・ファイルを処理する際には通常の GNU C の規約に従います。
__STDC__
を未定義とすること。
C++ から C へのトランスレータを使ってコンパイルするよう書かれたプログラムは、
C コンパイラによって定義され、
それ以降使われることになる __STDC__
の値を受け取ります。
このようなプログラムは、
コンパイラが使う C プリプロセッサの種類を決定するのに __STDC__
をテストしなければなりません。
プログラムの中で ANSI C 方式でのトークン連結を行うべきか、
昔から使われている方式のトークン連結を行うべきかを決めるのにこれが必要です。
これらのプログラムは、
__STDC__
が定義されていれば GNU C++ でも正しくコンパイルできます。
__STDC__
が定義されていなければコンパイルできないでしょう。
さらに、
多くのヘッダ・ファイルは、
昔から使われている形式ではなく ANSI C 形式のプロトタイプを提供するように書かれています。
これらのヘッダ・ファイルの多くは、
__STDC__
が定義されてさえいれば、
変更することなく C++ で使えます。
__STDC__
が定義されていなければ、
これらのヘッダ・ファイルはすべてコンパイルに失敗することになり、
C++ コンパイラが使われていることをも明示的にテストするよう変更する必要が出てくるでしょう。
void func (int, int); int i = 2; func (i++, i++);インクリメントがある特定の順序で評価されるという保証は (C と C++ のどちらの標準言語定義においても) ありません。 どちらのインクリメントが最初に実行されてもおかしくありません。
func
は、
`2, 3' という引数を受け取る可能性もありますし、
`3, 2' という引数を受け取る可能性もあります。
場合によっては、
`2, 2' という引数を受け取る可能性すらあります。
GNU コンパイラは、 2つの種類の診断を出力することができます。 エラーと警告です。 それぞれ異なる目的があります。
警告は、 プログラムが本当に意図されたとおりのことを行っているかどうかチェックして確認するべき危険な箇所を示しているかもしれません。 あるいは、 古くなってしまった機能を使っている箇所や、 GNU C や GNU C++ の標準的ではない機能を使っている箇所を示しているかもしれません。 多くの警告は、 ユーザが `-W' オプションのどれかを使ってその警告を明示的に要求した場合にのみ発行されます (例えば、 `-Wall' は様々な有用な警告を要求するオプションです)。
GNU CC は常に、 可能であればプログラムをコンパイルしようと試みます。 プログラムの意味するところが明確であれば、 (例えば) 単に標準に正しく準拠していないという理由だけで、 いわれもなくプログラムを拒否するようなことはしません。 しかし、 C や C++ の標準によってある特定の拡張が明示的に禁止されている場合があり、 そのような場合には、 標準に準拠するコンパイラは診断メッセージを出力しなければなりません。 `-pedantic' オプションは、 このような場合に警告を出力するよう GNU CC に通知するものです。 一方、 `-pedantic-errors' はこのような場合に警告ではなくエラーを出力するよう指示します。 このことは、 ANSI に準拠していないすべての構文に対して警告やエラーが出力されることを意味しているわけではありません。
これらの点に関する詳細、 および、 関連するコマンドライン・オプションの詳細については、 See section 警告を要求もしくは抑制するオプション。