警告を要求/抑止する オプションの補足
-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にまとめます.

〔表4〕警告を要求・抑止するオプション

*          *

 次回は,「コード生成規約に対するオプション」の補足,「最適化オプション」の補足を行う予定です.

Copyright 2003 岸 哲夫

Copyright 1997-2017 CQ Publishing Co.,Ltd.

 

NEW記事内インデックス    連載インデックスはこちら   Interfaceのトップ
GCC3.3をインストールする方法/C言語の方言を扱うオプション の補足/LINK関連のオプションの補足
プリプロセッサ関連のオプションの補足
◆警告を要求/抑止する オプションの補足