Oracle® Developer Studio 12.5: C ユーザーズガイド

印刷ビューの終了

更新: 2016 年 7 月
 
 

5.6 lint の参考情報と例

lint が行う検査、lint ライブラリ、および lint フィルタなどに関する lint の参考情報について説明します。

5.6.1 lint が行う診断

矛盾した使用、移植不可能なコード、疑わしい構造という 3 つの広範な条件カテゴリについての lint 固有の診断が発行されます。このセクションでは、これらのそれぞれの領域での lint の動作の例を確認し、それらが発生させる問題に可能な対応を推奨します。

5.6.1.1 整合性の検査

ファイル全域とファイル内部における変数、引数、関数の矛盾した使用を検査します。概して lint が古いスタイルの関数に対して検査していたのと同様に、プロトタイプの使用、宣言、引数を検査します。プログラムが関数プロトタイプを使用していない場合、lint は関数の呼び出しごとにコンパイラより厳しく引数の数と型を検査します。lint は、[fs]printf()[fs]scanf() の制御文字列の変換指示子と引数の不一致も識別します。

例:

  • lint はファイル内で、呼び出し元の関数に値を提供することなく終了する void でない関数に、フラグを設定します。以前、プログラマは fun() {} のように戻り型を省略することによって「関数は値を返さない」ということを示しました。この規約はこのコンパイラにとって意味がなく、fun() が戻り値の型 int を持つとみなします。この問題を解決するには、戻り型 void の関数として宣言します。

  • lint はファイルから、void でない関数が値を戻さないけれどもあたかもそうしたかのように式内で使用されている場合や、関数がときどきまたは常に無視される値を戻すという反対の問題を検出します。値が常に無視される場合は、関数定義内に非効率性が存在している可能性があり、値がときどき無視されることは、不適切なプログラミングスタイル (典型的には、for エラー条件を検査しない) である可能性があります。strcat()strcpy()、および sprintf() のような文字列関数や、printf()putchar() のような出力関数の戻り値を検査する必要がない場合、問題となる呼び出しは void にキャストしてください。

  • lint は、宣言されているけれども定義または使用されていない、使用されているけれども定義されていない、または定義されているけれども使用されていない変数や関数を洗い出します。一緒に読み込まれる一部のしかしすべてではないファイル群に lint が適用されると、次の状況の関数または変数についてエラーメッセージを発行します。

    • それらのファイルで宣言されているけれどもほかの場所で定義または使用されている。

    • それらのファイルで使用されているけれどもほかの場所で定義されている。

    • それらのファイルで定義されているけれどもほかの場所で使用されている。

    1 つ目の状況を抑制するには -x オプションを、後者の 2 つを抑制するには -u を呼び出します。

5.6.1.2 移植性の検査

移植性がない一部のコードはデフォルトの動作として lint によってフラグが付けられ、その他のいくつかのケースは -p または -pedantic を指定して lint が呼び出された場合に診断されます。lint は ISO C 規格に一致しない言語構造を検査します。-p および -pedantic の下で発行されるメッセージについては、lint ライブラリを参照してください。

例:

  • 一部の C 言語の実装では、signed または unsigned のどちらも明示的に宣言されていない文字変数は、符号付き (signed) の量として扱われ、通常は -128 ~ 127 の範囲になります。ほかの実装では、これらは負にならない量として扱われ、通常は 0 ~ 255 の範囲になります。文字変数が負でない値を取るマシンでは、次のテスト (EOF の値が -1) は常に失敗します。

    char c;
    c = getchar();
    if (c == EOF) ...

    -p を指定して呼び出された lint は、単純な char が負の値を持つ可能性があることを暗示する比較をすべて検査します。ただし、この例で csigned char として宣言すると、問題ではなく診断が除去されます。getchar() は可能なすべての文字と独自の EOF 値を戻す必要があるため、char には値を格納できません。この例は、処理系ごとに定義される符号拡張から生ずるおそらくもっとも一般的なものですが、lint の移植性オプションを注意深く使用すると移植性に関係しないバグを発見するのに役立つ可能性があることを示しています。ここでは cint で宣言します。

  • 同様の問題がビットフィールドにもあります。 定数値がビットフィールドに代入される場合、その値を保持するにはフィールドが小さすぎる場合があります。int 型のビットフィールドを符号なし (unsigned) の量として取り扱うマシンでは、int x:3 の範囲で許可される値が 0 から 7 であるのに対し、符号付き (signed) の量として取り扱うマシンでは -4 から 3 になります。ただし、型 int として宣言された 3 ビットのフィールドは、後者のマシン上で値 4 を保持できません。-p を指定して呼び出された lint は、unsigned int または signed int 以外のすべてのビットフィールド型にフラグを付けます。これらのみが、移植可能なビットフィールド型です。コンパイラは、ビットフィールドの型 intcharshort、および long をサポートしますが、これらは unsignedsigned またはそのどちらでもない場合があります。さらに enum のビットフィールドの型もサポートします。

  • 大きなサイズの型が小さなサイズの型に代入されると、問題が発生することがあります。有効なビットが切り捨てられると正確な値を保持できなくなります。

    short s;
    long l;
    s = l;

    lint は、デフォルトでは、このようなすべての代入にフラグを付けます。この診断は、–-a オプションを呼び出すことによって抑制できます。どのオプションを指定して lint を呼び出しても、ほかの診断をも抑制する可能性があることに注意してください。2 つ以上の診断を抑制するオプションについては、lint ライブラリにあるリストを参照してください。

  • あるオブジェクト型へのポインタをより厳格な整列要件を持つオブジェクト型へのポインタにキャストすると、移植性が損なわれる可能性があります。大部分のマシンでは、char が任意のバイト境界から開始できるのに対し、int はそうできないため、lint は次の例にフラグを付けます。

    int *fun(y)
    char *y;
    {
        return(int *)y;
    }

    この診断は、-h を指定して lint を呼び出すことによって抑制できますが、この場合も、ほかのメッセージを無効にしている可能性があります。この場合もまた、ほかのメッセージを抑制する可能性があります。汎用ポインタ void * を使用すればほかの影響を回避することができます。

  • ISO C は、複雑な式の評価順序を定義していません。この意味は、関数呼び出し、入れ子になった代入文、またはインクリメントとデクリメント演算子から副作用が生じる場合 (すなわち、式評価の副作用として変数が変更される時)、副作用の生じる順序はマシンへの依存度が高いということです。デフォルトでは、lint は副作用で変更された変数と同一式内でほかの場所に使用される変数にフラグを付けます。

    int a[10];
    main()
    {
        int i = 1;
        a[i++] = i;
    }

    この例では、a[1] の値は、あるコンパイラでは 1 になり、別のコンパイラでは 2 になる可能性があります。ビット単位の論理演算子 & を演算子 && の代わりに誤って使用すると、この診断が呼び出されることがあります。論理

    if ((c = getchar()) != EOF & c != ’0’)

5.6.1.3 疑わしい言語構造

lint は、適正であるが、プログラマが意図した内容を表現していない可能性がある構造の集まりにフラグを付けます。例:

  • unsigned 変数は常に負ではない値を持ちます。このため、次のテストは常に失敗します。

    unsigned x;
    if (x < 0) ...

    次のテスト:

    unsigned x;
    if (x > 0) ...

    これは次と同義です。

    if (x != 0) ...

    この結果は意図したアクションではない可能性があります。lint は、unsigned 変数と、負の定数または 0 との疑わしい比較にフラグを付けます。unsigned 変数を負数のビットパターンと比較するには、その負数を unsigned にキャストします。

    if (u == (unsigned) -1) ...

    または、接尾辞 U を使用します。

    if (u == -1U) ...
  • lint は、副作用が予想されるコンテキストで使用される副作用のない式、すなわちプログラマが意図したことを表現していない式にフラグを付けます。代入演算子が予想されるところ、つまり副作用が予想されたところで等価演算子が見つかるときは追加の警告が発行されます。

    int fun()
    {
        int a, b, x, y;
        (a = x) && (b == y);
    }
  • 演算子の優先度を間違って解釈することにより、不正確な結果になる可能性があるため、lint は、論理演算子とビット単位の演算子 (具体的には、&|^<<>>) の両方が混在する式に括弧を入れるように注意を与えます。たとえば、ビット単位 & の優先度は論理 == より低いため、次の式:

    if (x & a == 0) ...

    は次のように評価されます。

    if (x & (a == 0)) ...

    この結果はおそらく、意図されたものではありません。-h を指定して lint を呼び出すと、診断が無効になります。

5.6.2 lint ライブラリ

lint ライブラリを使用して、呼び出したライブラリ関数とプログラムとの互換性 (関数戻り型の宣言、関数が期待する引数の数と型など) を検査できます。標準 lint ライブラリは、C 言語処理系で供給されるライブラリに対応し、一般にはシステムの標準位置であるディレクトリに格納されています。慣例では、lint ライブラリは llib-lx.ln という形の名前を持ちます。

lint 標準 C ライブラリ、llib-lc.ln は、lint コマンド行の末尾にデフォルトで追加されます。それを使用した互換性の検査は、-n オプションを呼び出すことによって抑制できます。その他の lint ライブラリは、-l への引数としてアクセスされます。この例では、lint ライブラリ llib-lx.ln との互換性について、file1.cfile2.c 内の関数と変数の使用法を調べるよう lint に指示します。

% lint -lx file1.c file2.c

定義だけからなるライブラリファイルは、厳密に通常のソースファイルおよび通常の .ln ファイルとして処理されます。ただし、ライブラリファイル内で関数と変数が矛盾した状態で使用されるか、またはライブラリファイルで定義されているけれどもソースファイルで使用されない関数と変数に対しては警告を出しません。

独自の lint ライブラリを作成するには、C ソースファイルの先頭にディレクティブ NOTE(LINTLIBRARY) を挿入してから、-o オプションを指定し、さらに -l にライブラリ名を指定してそのファイルに対して lint を呼び出します。次の例では、NOTE(LINTLIBRARY) が先頭にあるソースファイル内の定義のみが、ファイル llib-lx.ln に書き込まれます。

% lint -ox file1.c file2.c

lint -occ -o は類似しています。ライブラリは、同様に関数プロトタイプ宣言のファイルから作成できますが、NOTE(LINTLIBRARY)NOTE(PROTOLIB(n)) の両方が宣言ファイルの先頭に挿入されている必要があります。 n が 1 の場合、プロトタイプ宣言は古いスタイルの定義と同様にライブラリ .ln ファイルに書き込まれます。n がデフォルトの 0 の場合、処理はキャンセルされます。-y を指定した lint の呼び出しは、lint ライブラリを作成するためのもう 1 つの方法です。次のコマンド行の場合は、その行に指定された各ソースファイルが NOTE(LINTLIBRARY) で開始しているかのように扱われ、その定義だけが llib-lx.ln に書き込まれます。

% lint -y -ox file1.c file2.c

デフォルトでは、lint は標準位置で lint ライブラリを検索します。標準の場所以外のディレクトリで lint ライブラリを検索するよう lint に指示するには、-L オプションでそのディレクトリのパスを指定します。

% lint -Ldir -lx file1.c file2.c

拡張モードでは、lint は基本モードで生成される .ln ファイルより多くの情報が格納された .ln ファイルを生成します。拡張モードの lint は、基本モードまたは拡張モードのどちらの lint で生成された .ln ファイルでもすべて読み取って理解できます。基本モードの lint は、基本モードの lint を用いて生成された .ln ファイルだけを読み取って理解できます。

デフォルトでは、lint/lib および /usr/lib ディレクトリのライブラリを使用します。これらのライブラリは基本 lint 形式です。makefile を一度実行して新しい形式の拡張 lint ライブラリを作成すれば、拡張 lint をより効率的に利用することができます。makefile を実行して新しいライブラリを作成するには、次のコマンドを使用してください。

% cd install-directory/prod/src/lintlib; make

ここで、install-directory はインストールディレクトリです。makefile の実行後、lint/lib または /usr/lib ディレクトリ内のライブラリの代わりに、拡張モードの新しいライブラリを使用します。

指定されたディレクトリが標準の場所の前に検索されます。

5.6.3 lint フィルタ

lint フィルタは、プロジェクト固有のポストプロセッサで、通常は awk スクリプトや類似のプログラムを使用して lint の出力を読み取り、プロジェクトが特に真の問題を識別していないと判断したメッセージを捨てます (たとえば、ときどきまたは常に無視される値を返す文字列関数などです)。lint オプションと指令が出力に対して十分な制御が提供しないときは、lint フィルタはカスタマイズされた診断レポートを生成します。

lint の 2 つのオプションはフィルタを開発する際に特に役立ちます。

  • -s オプションを指定すると、複合診断が、診断された問題が発生するたびに発行される 1 行の単純なメッセージに変換されます。この解析されたメッセージ書式は awk スクリプトによる分析に適しています。

  • -k オプションを指定すると、ソースファイル内に記述した特定のコメントが出力されます。これは、プロジェクトの決定をドキュメント化する場合とポストプロセッサの動作を指定する場合の両方で役立つ可能性があります。コメントが予想される lint メッセージを示していて、報告されたメッセージがそれと同一であった場合、メッセージは除かれます。-k を使用するには、コメントを付けるコードの直前の行に NOTE(LINTED(msg)) ディレクティブを挿入します。ここで、msg は、-k を指定して lint が呼び出されたときに出力されるコメントを示します。

    NOTE(LINTED(msg)) が含まれているファイルに対して -k が呼び出されない場合の lint の動作については、表 16にあるディレクティブのリストを参照してください。