● -Wno-format-zero-length
-Wformatオプションを使用するときに,同時にこのオプションを指定するとprintfなどのフォーマットの長さが0でも警告しません(リスト8).-Wformatに関しては連載第3回で解説しています.
〔リスト8〕-Wformatオプションの警告を一部抑止する例 (test168.c)
|
/*
*-Wformatオプションの警告を一部抑止する
*/
#include <stdio.h>
int main(void)
{
long b = 100000;
printf("%f\n",b);
printf("","test168\n");
return 0;
}
|
|
コンパイルの結果を以下に示します.
$gcc -Wformat -Wno -format-zero -length test168.c
test168.c: 関数 `main' 内:
test168.c:8: 警告: フォーマットはdoubleですが,引数はdifferent typeです(引数2)
$gcc -Wformat test168.c
test168.c: 関数 `main' 内:
test168.c:8: 警告: フォーマットはdoubleですが,引数はdifferent typeです(引数2)
test168.c:9: 警告: zero-length printf format string
$gcc test168.c
$
● -Wnonnull
第7回の連載で触れましたが,GCCでは「関数属性の宣言」が可能です.この拡張機能の3.2.2になったことによる追加は後述します.
追加された関数属性の宣言の中に関数の引き数がNULLではまずい場合,エラーメッセージで警告する機能があります(リスト9).
〔リスト9〕関数の引き数がNULLではまずい場合にエラーメッセージで警告する例(test169.c)
|
/*
*-Wnonnull関数の引き数がNULLではまずい場合に
*エラーメッセージで警告する
*/
#include <stdio.h>
void test_memcpy(void *dest, const void *src, size_t len)
__attribute__((nonnull (1, 2))); //関数属性の宣言
int main(void)
{
void *a;
test_memcpy(NULL,NULL,100);
test_memcpy((void*)strcpy(NULL,NULL),a,100);
return 0;
}
void test_memcpy(void *dest, const void *src, size_t len)
{
}
|
|
その機能を付加したい場合に,関数属性の宣言をして,このオプションを使用します.
コンパイルの結果を以下に示します.
$gcc -Wnonnull test169.c
test169.c: 関数 `main' 内:
test169.c:12: 警告: null argument where non-null required(arg 1)
test169.c:12: 警告: null argument where non-null required(arg 2)
$
ソースを見てわかるように,関数属性の宣言をしても,明白にNULLを指定した場合のみ作用します.“strcpy(NULL,NULL)”の戻り値は確実にNULLだと思いますが,チェックすることはできません.もちろん実行時にNULLになるような式を指定してもチェックできません.
● -Wswitch-default
これは,switch文のdefault動作を指定する文がない場合に警告させるためのオプションです(リスト10).
〔リスト10〕switch文のデフォルトがない場合に警告する例 (test170.c)
|
/*
*switch文のデフォルトがない場合に警告
*/
#include <stdio.h>
int main(void)
{
int num;
num = 1;
switch (num)
{
case 1:
printf("びろーん\n");
break;
case 2:
printf("がちょーん\n");
break;
}
num = 9;
switch (num)
{
case 1:
printf("びろーん\n");
break;
case 2:
printf("がちょーん\n");
break;
default:
printf("違います\n");
break;
}
return 0;
}
|
|
コンパイルと実行の結果を,以下に示します.
$gcc test170.c -o test170
$gcc -Wswitch-default test170.c -o test170
test170.c: 関数 `main' 内:
test170.c:17: 警告: switch missing default case
$ ./test170
びろーん
違います
$
● -Wswitch-enum
C言語の型に「列挙型」と呼ばれるものがあります.switch文のインデックスに列挙型の項目すべてが指定されていない場合に,警告させるためのオプションです(リスト11).
〔リスト11〕列挙型の項目が足りない場合に警告する例(test171.c)
|
/*
*列挙型の項目が足りない場合に警告
*/
#include <stdio.h>
int main(void)
{
enum tag1 { a , b , c , d , e } ;
enum tag1 x ;
switch (x)
{
case a:
printf("その1\n");
break;
case b:
printf("その2\n");
break;
case c:
printf("・・・・・・\n");
break;
case e:
printf("まだまだ\n");
break;
}
return 0;
}
|
|
コンパイルの結果を次に示します.
$gcc -Wswitch-enum test171.c -o test171
test171.c: 関数 `main' 内:
test171.c:23: 警告: enumeration value`d'not handled in switch
$
このように,列挙型の項目の値をすべてswitch文の処理に対応させたい場合に,指定すると便利です.抜けをチェックできます.
● -Wstrict-aliasing
ANSIでも以前から規定されていますが,あるオブジェクトに格納された値にアクセスする方法は,次のうちいずれか一つの型をもつ左辺値によるものだけでなければなりません.
・オブジェクトの宣言された型
・オブジェクトの宣言された型の修飾版
・オブジェクトの宣言された型と対応する符合付き整数型または符合なし整数型
・メンバの中にこれらの型の一つを含む集成体または共用体型
・文字型
この規定を破っても警告は出ません.結果が不定になるだけです.
また,あるオブジェクトと別の名前をもつオブジェクトは,違うアドレスに配置されているという前提で,コンパイラは最適化を行います.規則違反の別名定義があると,最適化をすることができなくなります.このオプションを指定すると,別名規則を破っていて最適化に不都合な場合に警告します(リスト12).
〔リスト12〕別名規則を破っていて最適化に不都合な場合に警告する例(test172.c)
|
/*
*別名規則を破っていて最適化に不都合な場合に警告
*/
#include <stdio.h>
int main(void)
{
int a = 100;
register int b = 200;
double *x;
x = (double*)&a;
x = (double*)&b;
printf("%d\n",a);
printf("%f\n",x);
return 0;
}
|
|
コンパイルの結果を以下に示します.
$gcc -fstrict-aliasing -Wstrict-aliasing test172.c
test172.c: 関数 `main' 内:
test172.c:10: 警告: dereferencing type-punnedpointer will break strict-aliasing rules
test172.c:11: 警告: register変数 `b' のアドレスが要求されました
test172.c:11: 警告: dereferencing type-punned pointer will break strict-aliasing rules
$gcc test172.c
test172.c: 関数 `main' 内:
test172.c:11: 警告: register変数 `b' のアドレスが要求されました
$
もともと別名定義のできるわけがないレジスタ変数を,別名でアクセスしようとすると,別の警告が出ます.この件に関しては,このオプションを外しても警告を出します.
● -Wno-div-by-zero
このオプションを付けると,明らかなゼロ除算がソース中にあっても警告しないようにします.NANやINFを導出させるのに使用することがあります.連載の第10回でも少し触れましたがNAN,INFはマクロで定義されています.NANは数値例外であり,INFは無限大です(リスト13).
〔リスト13〕ゼロ除算で警告しないようにする例(test173.c)
|
/*
*ゼロ除算で警告しないようにする
*/
#include <stdio.h>
#include <math.h>
int main(void)
{
float f = 10.0/0;
double d = 0.0/0.0;
printf("%G\n",f);
printf("%G\n",d);
}
|
|
コンパイルの結果を以下に示します.
$gcc test173.c -o test173
test173.c: 関数 `main' 内:
test173.c:8: 警告: division by zero
$gcc -Wno-div-by-zero test173.c -o test173
$ ./test173
NAN
INF
$
● -W
まぎらわしいですが,これは大文字のWです.このオプションは,雑多な文法ミスに警告を発してくれます(リスト14).
〔リスト14〕-Wオプションでエラーになる例(その1)(test178.c)
|
/*
*-Wオプションでエラーになる例
*/
#include <stdio.h>
int main(void)
{
return 0;
}
foo (a)
{
if (a > 0)
return a;
}
|
|
コンパイルの結果を以下に示します.
$gcc -W test178.c -o test178
test178.c: 関数 `foo' 内:
test178.c:10: 警告: type of`a' defaults to`int'
$
これは引き数に型を指定しなかったため,int型だと受け取られコンパイルした例です(リスト15).
〔リスト15〕-Wオプションでエラーになる例(その2)(test179.c)
|
/*
*-Wオプションでエラーになる例
*/
#include <stdio.h>
int main(void)
{
char tbl[50];
int i = 10;
int j = 20;
tbl[i,j] = 'a';
if (tbl[10] == 'a' )
{
printf("tbl[10]は'a'\n");
}
if (tbl[20] == 'a' )
{
printf("tbl[20]は'a'\n");
}
return 0;
}
|
|
コンパイルと実行の結果を以下に示します.
$gcc -W test179.c -o test179
test179.c: 関数 `main' 内:
test179.c:10: 警告: left-hand operand of comma expression has no effect
$ ./test179
tbl[20]は'a'
$gcc test179.c -o test179
$ ./test179
tbl[20]は'a'
$
たとえば,COBOLプログラマが思い込みで二次元テーブルの使い方を間違え,カンマで区切ってしまった場合に警告ができます(リスト16).
〔リスト16〕-Wオプションでエラーになる例(その3)(test180.c)
|
/*
*-Wオプションでエラーになる例
*/
#include <stdio.h>
int main(void)
{
unsigned int i = 9;
if (i < 0 ) return 1;
return 0;
}
|
|
コンパイルの結果を以下に示します.
$gcc -W test180.c -o test180
test180.c: 関数 `main' 内:
test180.c:8: 警告: comparison of unsigned
expression<0 is always false
$gcc test180.c -o test180
$
符号なし数値をゼロより小さいか?と比較することは無駄です.これも警告されます(リスト17).
〔リスト17〕-Wオプションでエラーになる例(その4)(test181.c)
|
/*
*-Wオプションでエラーになる例
*/
#include <stdio.h>
int main(void)
{
unsigned int x;
unsigned int y;
unsigned int z;
if (x<=y<=z) return 1;
return 0;
}
|
|
コンパイルの結果を以下に示します.
$gcc -W test181.c -o test181
test181.c: 関数 `main' 内:
test181.c:10: 警告: comparisons like X<=Y<=Z
do not have their mathematical meaning
$gcc test181.c -o test181
$
これは文法的に意味のない比較なので,警告されます(リスト18).
〔リスト18〕-Wオプションでエラーになる例(その5)(test182.c)
|
/*
*-Wオプションでエラーになる例
*/
#include <stdio.h>
const int test(void);
int main(void)
{
return test();
}
const int test(void)
{
return 10;
}
|
|
コンパイルの結果を次に示します.
$gcc -W test182.c -o test182
test182.c:5: 警告: type qualifiers ignored on function return type
test182.c:11: 警告: type qualifiers ignored on function return type
$gcc test182.c -o test182
関数の戻り値にconst指定しても,lvalueとして扱われるわけではないので意味がありません.警告されます.
-Wオプションで,以上のような細かい警告を出力することが可能になります.
● -Wdisabled-optimization
このオプションは,コンパイラが最適化することに非常に負荷がかかるようなソースを扱い,最適化を断念したときに警告を出すものです.たとえば一つの関数が数千行あったり,条件のネストが異常に深かったりした場合です.
● -Wfloat-equal
OSの環境にも依存しますが,通常は浮動小数点値を“==”で比較することは危険です.このオプションはそのような計算式に警告を行います(リスト19).
〔リスト19〕浮動小数点値を==で比較する例(test183.c)
|
/*
*浮動小数点値を=で比較する例
*/
#include <stdio.h>
int main(void)
{
float test1 = 1.0f/4.0f;
float test2 = 0.25f;
if (test1 == test2 )
{
printf("等価です\n");
}
return 0;
}
|
|
コンパイルの結果を以下に示します.
$gcc -Wfloat-equal test183.c -o test183
test183.c: 関数 `main' 内:
test183.c:9: 警告: comparing floating point with==or!=is unsafe
$
● -Wmissing-format-attribute
このオプションは-Wformatとともに使用し,printf系の関数に指定するフォーマット文字列をチェックするものです.フォーマット文字列が適切でない場合に警告します(リスト20).
〔リスト20〕printfなどのフォーマットをチェックする例(test184.c)
|
/*
*printfなどのフォーマットをチェックする
*/
int main()
{
printf("%d\n",9);
printf("%a\n",9);
printf("",9);
return 0;
}
|
|
コンパイルの結果を以下に示します.
$gcc -Wmissing-format-attribute -Wformat test184.c -o test184
test184.c: 関数 `main' 内:
test184.c:7: 警告: フォーマットはdoubleですが,
引数はdifferent typeです (引数2)
test184.c:8: 警告: zero-length printf format string
● -Wno-deprecated-declarations
このオプションは,関数属性の補足で解説します.
● -Wno-multichar
このオプションを指定すると,デフォルトで警告を出すマルチ文字定数を含んだソースに警告しなくなります.
マルチ文字定数を使うと可搬性に問題が起きるので,使わないほうがよいと思います.
● -Wpacked
構造体に対してパックを行っても,その効果がない場合に警告します(リスト21,リスト22).
〔リスト21〕構造体に対してパックを行っても,その効果がない場合の例(test185.c)
|
/*
*構造体に対してパックを行っても,
*その効果がない場合の例
*/
int main()
{
struct str01
{
int x;
char a, b, c, d;
} __attribute__((packed));
struct str02
{
char a;
struct str01 data1;
};
struct str02 data2;
data2.a = 'a';
data2.data1.x = 0;
data2.data1.a = '0';
data2.data1.b = '0';
data2.data1.c = '0';
data2.data1.d = '0';
return 0;
}
|
|
〔リスト22〕生成されたアセンブラソース(test185.s)
|
.file "test185.c"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movb $97, -24(%ebp)
movl $0, -23(%ebp)
movb $48, -19(%ebp)
movb $48, -18(%ebp)
movb $48, -17(%ebp)
movb $48, -16(%ebp)
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (GNU) 3.3"
|
|
コンパイルの結果を以下に示します.
$gcc -Wpacked test185.c -S
test185.c: 関数 `main' 内:
test185.c:8: 警告: packed attribute is unnecessary for`x'
test185.c:9: 警告: packed attribute is unnecessary for`a'
test185.c:9: 警告: packed attribute is unnecessary for`c'
$
● -Wpadded
GCCでは構造体の各要素を4バイト境界に配置します.配置できない場合にパディングしますが,その状態になったときに警告します(リスト23,リスト24).
〔リスト23〕構造体に対しパディングを行う例(test186.c)
|
/*
*構造体に対しパディングを行う例
*/
int main()
{
struct str01
{
char a;
int b;
char c;
};
struct str01 data1;
data1.a = 'a';
data1.b = 0;
data1.c = 'a';
printf("%d\n",sizeof(data1));
return 0;
}
|
|
〔リスト24〕生成されたアセンブラソース(test186.s)
|
.file "test186.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $40, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movb $97, -24(%ebp)
movl $0, -20(%ebp)
movb $97, -16(%ebp)
movl $12, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (GNU) 3.3"<
|
|
コンパイルと実行の結果を以下に示します.
$gcc -Wpadded test186.c -o test186
test186.c: 関数 `main' 内:
test186.c:9: 警告: padding struct to align`b'
test186.c:11: 警告: padding struct size to alignment boundary
$ ./test186
12
$gcc -Wpadded test186.c -S
test186.c: 関数 `main' 内:
test186.c:9: 警告: padding struct to align`b'
test186.c:11: 警告: padding struct size to alignment boundary
$
● -Wunreachable-code
ソース中に実行されないコードがあるときに警告します.このようなあからさまな例はともかく,デバッグ中に実行されないコードが出現することは珍しくないと思います.そのような場合に警告してくれます(リスト25,リスト26).
〔リスト25〕実行されないコードに警告する例(test187.c)
|
/*
*実行されないコードに警告する例
*/
int main()
{
struct str01
{
char a;
int b;
char c;
};
struct str01 data1;
data1.a = 'a';
data1.b = 0;
data1.c = 'a';
printf("%d\n",sizeof(data1));
return 0;
printf("実行されません\n");
}
|
|
〔リスト26〕生成されたアセンブラソース(test187.s)
|
.file "test187.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $40, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movb $97, -24(%ebp)
movl $0, -20(%ebp)
movb $97, -16(%ebp)
movl $12, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (GNU) 3.3"
|
|
コンパイルの結果を次に示します.
$gcc -Wunreachable-code test187.c -S
test187.c: 関数 `main' 内:
test187.c:18: 警告: will never be executed
$
● -Wunused-value
使用していない値がある場合に警告します.この例では計算値を変数に入れ忘れているのかもしれません(リスト27,リスト28).
〔リスト27〕実行されないコードに警告する例(test188.c)
|
/*
*使用していない値が
*ある場合に警告する例
*/
int main()
{
1 + 2 + 3;
return 0;
}
|
|
〔リスト28〕生成されたアセンブラソース(test188.s)
|
.file "test188.c"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (GNU) 3.3"
|
|
コンパイルの結果を以下に示します.
$gcc -Wunused-value test188.c -S
test188.c: 関数 `main' 内:
test188.c:6: 警告: statement with no effect
$
生成されたアセンブラソース上で,意味のない値は排除されています.
以上の警告を要求・抑止するオプションを表4にまとめます.
次回は,「コード生成規約に対するオプション」の補足,「最適化オプション」の補足を行う予定です.
Copyright 2003 岸 哲夫
Copyright 1997-2024 CQ Publishing Co.,Ltd.
 記事内インデックス 連載インデックスはこちら Interfaceのトップ |
|
|
|