さて次は,警告を要求/抑止するオプションについて説明および検証します.
警告とは,誤りではないが,危険な処理であったり誤りのある可能性を示唆している構文を報告するメッセージです.
-Wで始まるオプションを指定することで,多くの特定の警告を要求することができます.これらの特定の警告オプションのそれぞれには,-Wno-で始まる警告を出力させないための否定形式があります.
● -fsyntax-only
ソースに構文エラーがあるか否かをチェックするだけで,それ以上のことは行いません.
● -pedantic
厳密なANSI CおよびISO C++により要求される警告をすべて出力します.禁止されている拡張機能を使うプログラムをすべて拒絶します.正当なANSI CプログラムおよびISO C++プログラムであれば,このオプションの指定の有無にかかわらず正しくコンパイルされるはずです.しかし,このオプションが指定されないと,特定のGNU拡張機能もまたサポートされることになります.
GNU拡張機能の例として,typedefで型を表す変数のようなものを作ることができます.
リスト12のようにすると,ty型は変数xと同じ型(つまりint型)になり,その型で変数yが宣言されます.
〔リスト12〕型を代入する拡張機能の例(test15.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int x;
typedef ty = x;
ty y;
return 0;
}
|
|
このソースを-pedanticオプションを付加してコンパイルするとエラーになります.付加しないと拡張機能は有効になり,
コンパイル可能です.
$ gcc -pedantic test15.c
test15.c:1: warning: carriage return in preprocessing directive
test15.c:2: warning: carriage return in preprocessing directive
test15.c:3: warning: carriage return in preprocessing directive
test15.c:4: warning: carriage return in source file
test15.c:4: warning: (we only warn about the first carriage return)
test15.c: In function main':
test15.c:7: typedef ty' is initialized
$ gcc test15.c
$
● -pedantic-errors
-pedanticと同様のチェックを行いますが,警告の代わりにエラーとします.
● -w
すべての警告メッセージを抑止して出力しません.
● -Wchar-subscripts
配列の添え字の型がcharなら警告を出します.これはよくある間違いの元となるでしょう.char型が環境によっては符号付きになることに注意を促すための警告です(リスト13).
〔リスト13〕配列の添え字の型に警告する例(test16.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char x;
char tbl[256];
x = 255;
tbl[x] = 'a';
return 0;
}
|
|
$ gcc test16.c
$ gcc -Wchar-subscripts test16.c
test16.c: In function main':
test16.c:9: warning: array subscript has type char'
$
このように,何もオプションを付けなければコンパイルは通ります.-Wchar-subscriptsを付加した場合はチェックして警告します.この例の場合,配列の添え字が255であるとプログラマは考えていますが,アセンブラコード(リスト14)を見てもわかるとおり,符号付charに255をセットしているので−1がセットされて配列の参照先は不定となり,プログラムに大きな影響を及ぼします.
〔リスト14〕test16.cをコンパイル後のアセンブラソース
|
.file "test16.c"
.version "01.01"
gcc2_compiled.:
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $280,%esp
movb $-1,-1(%ebp)
movsbl -1(%ebp),%eax
leal -260(%ebp),%edx
movb $97,(%eax,%edx)
xorl %eax,%eax
jmp .L2
.p2align 4,,7
.L2:
movl %ebp,%esp
popl %ebp
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU)
2.95.3 20010315 (release)"
|
|
● -Wcomment
コメント開始文字列/*が,/*形式のコメント中に現れた場合に警告を出します.
● -Wformat
printfやscanfなどの関数の呼び出し時のフォーマット文字列を検査し,指定された引き数の型とフォーマット文字列に矛盾がないかチェックします(リスト15).
〔リスト15〕フォーマット文字列の矛盾に警告する例(test17.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int y;
y = 500;
printf("%f\n",y);
printf("%d\n",y);
return 0;
}
|
|
$ gcc test17.c
$ ./a.out
0.000000
500
$ gcc -Wformat test17.c
test17.c: In function main':
test17.c:8: warning: double format,
different type arg (arg 2)
$
この例では,intの値に対してdoubleのフォーマット文字列で表示しようとしています.このようなミスをチェックできます.
● -Wimplicit-int
宣言に型指定がない場合に警告を出します(リスト16).
〔リスト16〕宣言に型指定がない場合に警告する例(test18.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
register i;
i = 500;
printf("%d\n",i);
return 0;
}
|
|
$ gcc test18.c
$ gcc -Wimplicit-int test18.c
test18.c: In function main':
test18.c:6: warning: type defaults to int'
in declaration of i'
$
上記の例ではregister変数iが暗黙にint型となっていますが,今後の言語仕様では明示的に型宣言をしなくてはならなくなります.今のうちにこの使い方はやめて,明示的に型宣言したほうが良いと思います.
● -Wimplicit-function-declaration,
-Werror-implicit-function-declaration
関数が宣言される前に使われている場合に警告またはエラーを出します(リスト17).
〔リスト17〕関数にプロトタイプ宣言がない場合に警告またはエラーにする例(test19.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
printf("%d\n",test());
return 0;
}
int test()
{
return 100;
}
|
|
$ gcc test19.c
$ gcc -Wimplicit-function-declaration test19.c
test19.c: In function main':
test19.c:6: warning: implicit declaration of function test'
$ gcc -Werror-implicit-function-declaration test19.c
test19.c: In function main':
test19.c:6: implicit declaration of function test'
$
あまり意味のないプログラム例ですが,-Wimplicit-function-declarationを指定してコンパイルしたものは
警告を出し,-Werror-implicit-function-declarationを指定した場合にはエラーになっています.
関数プロトタイプ宣言を付加することは関数の呼び出し時に引き数の数や型の間違いを見つけることに有効なので,ぜひ習慣づけておくと良いと思います.
● -Wimplicit
-Wimplicit-intと-Wimplicit-function-declarationを同時に指定したときと同じふるまいをします.
● -Wmain
mainの型に問題がある場合に警告を出します.mainは,intを返し,適切な型の引き数を0個か,2個か,3個を取る関数でなければなりません.この環境のGCCでは,このオプションはデフォルトとなっています(リスト18).
〔リスト18〕mainの型チェックを行う例(test20.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main()
{
printf("%s\n","test");
}
|
|
$ gcc -Wno-main test20.c
$ gcc -Wmain test20.c
test20.c:5: warning: return type of main' is not int'
$
意図的にmainの型を変えてコンパイルしたい場合には,-Wmainの否定形である-Wno-mainを付加してください.
● -Wmultichar
‘F1F0’のように,複数文字から構成される定数が使われていると警告を出力します.通常はコードの中にタイプミスがあることを示唆しています.このような定数は環境に対しての依存性が高いので,可搬性・可読性を高める意味から使用しないことをお勧めします.
● -Wparentheses
特定のコンテキストにおいて中括弧{}が省略されると警告を出力します.きちんと字下げを行い,if文に対するelseを省略せず混乱を避けて記述すれば問題がありません.後からプログラムを読んで混乱しないためにも,このオプションをつけてコンパイルすることは有意義だと思います.
リスト19は,if文の入れ子に対し文法的には正しいけれど読みにくいソースです.
〔リスト19〕if文の入れ子に対し文法的には正しいけれど読みにくいソース(test21.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int a;
int b;
int c;
a = 10;
b = 20;
c = 30;
if (a==10)
if (b==20)
if (c==20)
printf("%s\n","test1");
else
printf("%s\n","test2");
return 0;
}
|
|
$ gcc test21.c
$ gcc -Wparentheses test21.c
test21.c: In function main':
test21.c:13: warning: suggest explicit braces to avoid ambiguous else'
$
また,リスト20は,リスト19を書き直したソースです.
〔リスト20〕リスト19を書き直したソース(test22.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int a;
int b;
int c;
a = 10;
b = 20;
c = 30;
if (a==10)
{
if (b==20)
{
if (c==20)
{
printf("%s\n","test1");
}
}
}
else
{
printf("%s\n","test2");
}
return 0;
}
|
|
$ gcc test22.c
$ gcc -Wparentheses test22.c
$
リスト19のソースは-Wparenthesesオプションを付加すると警告を出力しますが,リスト20では問題ありません.
● -Wreturn-type
戻り値の型がデフォルトでintになるように定義された関数がある場合に常に警告を出力します.また,戻り値の型がvoidではない関数の中に,戻り値を取らないreturn文が一つでもあれば警告を出力します.
● -Wreturn-type
戻り値の型がデフォルトでintになるように定義された関数がある場合に警告を出力します.また,戻り値の型がvoidではない関数の中に戻り値を取らないreturn文が一つでもあれば,警告を出力します(リスト21).
〔リスト21〕戻り値の型がデフォルトでintになるように定義された関数の例(test23.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
printf("%d\n",test());
return 0;
}
test()
{
return 100;
}
|
|
$ gcc test23.c
$ gcc -Wreturn-type test23.c
test23.c:10: warning: return-type defaults to int'
$
● -Wswitch
switch文が列挙型のインデックスをもつ場合,対応するcaseが一つでも存在しない場合には,常に警告を出力します.しかしdefaultラベルが存在すると,この警告メッセージの出力は行われません.このオプションが使われると,列挙型の値の範囲にないcaseラベルが存在する場合にも,警告が出力されることになります.
● -Wtrigraphs
3文字表記が一つでも見つかると警告を出力します.もはやtrigraphは使わないほうが良いと思うので,このオプションは有意義だと思います.
● -Wunused
オブジェクトが定義されているものの,使用されていない場合に警告を出します.使っていない変数やラベルが沢山あるプログラムは,メンテナンスするのに苦労します.このオプションはぜひとも使用して不要なオブジェクトを取り除くべきでしょう.
いろいろな都合で,使用していない変数を削除したくない場合にその変数にunused属性を付けておけば,このオプションが有効でも警告を出しません.また,使われない関数パラメータに関する警告メッセージを出力させるためには,-Wと-Wunusedの両方を指定しなければなりません.ある式に関してこの警告を出力させないようにするには,単にその式をvoidにキャストしてください.
● -Wuninitialized
初期化されずに使われている自動変数について,警告を出力します.この警告は,最適化コンパイルにおいてのみ可能です.-Oを指定しなければ,この警告が出力されることはありません.
また,この警告は,レジスタを割り当てる候補となる変数に対してのみ出力されます.よってこの警告は,volatile宣言されている変数,アドレスが取られている変数,サイズが1,2,4,8バイトのいずれでもない変数に対して出力されることはありません.また,構造体,共用体,配列に対しては,たとえそれらがレジスタの中に割り当てられている場合でも,この警告が出力されることはありません.
以下に簡単な例をあげます(リスト22).
〔リスト22〕自動変数を初期化しないケースが考えられる例(test24.c)
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int Wday;
int Wday_save;
enum Days
{
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
Wday = 5;
switch (Wday)
{
case Sunday:
Wday_save = Sunday;
printf("%s\n","Sunday");
break;
case Monday:
Wday_save = Monday;
printf("%s\n","Monday");
break;
case Tuesday:
Wday_save = Tuesday;
printf("%s\n","Tuesday");
break;
case Wednesday:
Wday_save = Wednesday;
printf("%s\n","Wednesday");
break;
case Thursday:
Wday_save = Thursday;
printf("%s\n","Thursday");
break;
case Saturday:
Wday_save = Saturday;
printf("%s\n","Saturday");
break;
default:
Wday_save = Saturday;
}
printf("%d\n",Wday_save);
return 0;
}
|
|
$ gcc -Wuninitialized -O test24.c
test24.c: In function main':
test24.c:7: warning: Wday_save' might be used uninitialized in this function
$
この例でswitch文にdefaultを付ければ必ず初期化されるので,警告は発生しません.よくあるバグの元なので,このオプションは有意義に使用できると思います.
● -Wunknown-pragmas
GCCの知らない#pragma指示子が検出されたときに警告を出力します.これは他の環境で作られたソースを移行するときに有効です.
● -Wall
このオプションを指定すると,これまで説明してきたすべての-Wオプションをすべて指定したのと同義になります.
また,以下の問題に対して追加で警告メッセージを出力します.
・非volatile自動変数がlongjmpの呼び出しにより変えられる可能性がある場合
・関数が値なしでも値付きでもどちらでも戻ることができる場合
・式分やカンマ式の左辺に副作用がない場合.この警告を抑えるためには,使われない式をvoidにキャストする
・符号なしの値を<や<=でゼロと比較している場合
・x<=y<=zのような比較が現れた場合.これは(x<=y ? 1 : 0) <= zに等価となる.通常の数学的な記法とは異なる解釈になっていることに注意
・staticのような記憶クラス指定子が宣言の最初でないところにある
・-Wallか-Wunusedを合わせて指定すると,未使用の引き数について警告する
・符号付きの値と符号なしの値の比較により,符号付きの値が符号なしに変換されたとき,正しくない結果を生じる場合
● -Wtraditional
伝統的なCにおける解釈とANSI Cにおける解釈が異なる特定の構文に関して警告を出力します.
・マクロ本体の中の文字列定数の中にマクロ引き数が現れる場合.このようなとき,伝統的なCでは引き数の代替が行われるが,ANSI Cでは定数の一部となる
・あるブロックの中でexternal宣言されている関数が,そのブロックの終端よりも後で使われている場合
・switch文がlong型のオペランドをもつ場合
・staticな関数宣言の後にstaticではない関数宣言が存在する場合.このような構成を受け入れないコンパイラも数多く存在する
● -Wundef
#if指示子の中で未定義の識別子が評価されると,警告を出力します.
● -Wshadow
ある局所変数が別の局所変数を隠す場合に,常に警告を出力します.
● -Wid-clash-len
二つの別個の識別子の,先頭からlenにより示される数の文字が一致している場合に,常に警告を出力します.これは,変数の名称の頭8文字しか認識しないようなコンパイラを使わなくてはならないような場合に役立ちます.
● -Wlarger-than-len
lenにより示されるバイト数よりも大きいオブジェクトが定義されている場合に,常に警告を出力します.これは,環境によってメモリの消費を抑えなくてはならない場合などに役立ちます.
● -Wpointer-arith
関数型のサイズ,もしくはvoidのサイズに依存するものに対して警告を出力します.GNU Cはvoid *型のポインタや関数ポインタを使う計算の便宜のために,これらの型に1というサイズを割り当てています.
● -Wbad-function-cast
関数呼び出しが,マッチしない型にキャストされたときに警告します.
● -Wcast-qual
ポインタが,それが指す型から型修飾子を取り除いた型にキャストされると警告を出します.たとえば,const char *をchar *にキャストすると警告を出しますが,これはそうしなくてはならない状況が頻繁に起きると思います.このオプションを指定する際は注意して使ってください.
● -Wcast-align
ポインタの指す型に要求されるアライメントの値が大きくなるようなキャストがポインタに対して行われる場合に警告を出力します.
● -Wwrite-strings
文字列定数のアドレスをconst指定されないchar *型のポインタにコピーしようとすると警告が出力されます.
● -Wconversion
プロトタイプの存在により引き起こされる引き数の型変換が,それが存在しなかった場合に同じ引き数に対して行われるであろう変換と異なる場合に警告を出力します.
● -Wsign-compare
signedの値とunsignedの値との比較が,signedの値がunsignedに変換されることによって,正しくない結果をもたらす可能性がある場合に警告が出力されます.
● -Waggregate-return
構造体や共用体を返す関数が一つでも定義されていたり,呼び出されていると警告を出力します.
● -Wstrict-prototypes
関数が,引き数の型を指定されずに宣言もしくは定義されると,警告を出力します.
正しい関数プロトタイプ宣言を付加することは,関数の呼び出し時に引き数の数や型の間違いを見つけることに有効なので,ぜひ習慣付けておくと良いと思います.
● -Wmissing-prototypes
グローバルな関数が事前のプロトタイプ宣言なしで定義された場合に警告を出します.おもな目的は,ヘッダファイルで宣言されていないグローバルな関数を検出することにあります.
● -Wmissing-declarations
グローバルな関数が事前の宣言なしで定義された場合に警告を出します.おもな目的は,ヘッダファイルで宣言されていないグローバルな関数を検出することにあります.
● -Wmissing-noreturn
属性noreturnの候補になる可能性のある関数について警告します.これらはあくまで候補であって,絶対的なものではないことに注意してください.noreturn属性を追加する前に,関数が実際に決して戻らないことを自分で注意深く検証する必要があります.
● -Wredundant-decls
同じスコープ内で複数回宣言されているものがあれば警告します.
● -Wnested-externs
extern宣言が関数内にあると警告します.
● -Winline
関数がinline宣言されているか,-finline-functionsオプションが指定されているときに,インライン展開できない関数があれば警告を出します.
● -Wlong-long
long long型が使われていると警告を出します.これはデフォルトです.この警告メッセージを出さないようにするには,
-Wno-long-longを指定します.-Wlong-longと-Wno-long-longオプションは,-pedanticオプションが指定されている場合にのみ考慮されることになります.
なお,long long型はANSI規格には存在せず,GCCの拡張機能です.long long型定数は,定数の後にスペースを空けずに接尾語Lまたはlを2個指定します.
16ビットを超える値の整定数は接尾語Lを付けなくても自動的にlong型に格上げされますが,32ビットを超える 値の整定数はANSIとの互換性のため,自動的なlong long型への格上げが行われません.long long型整定数を記述する場合には,必ずLLを付ける必要があります.
● -Werror
すべての警告をエラーに変えます.
|