ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris Studio 12.3: C ユーザーガイド Oracle Solaris Studio 12.3 Information Library (日本語) |
lint が行う検査、lint ライブラリ、および lint フィルタなどに関する lint の参考情報について説明します。
lint 固有 の診断は、矛盾した使い方、移植不能のコード、疑わしい言語構造の 3 つの広い条件カテゴリ に対して表示されます。この節では、これらのそれぞれの領域での lint の動作の例を確認し、それらが発生させる問題に可能な対応を推奨します。
ファイル全域とファイル内部における変数、引数、関数の矛盾した使用を検査します。概して 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 オプションを呼び出してください。
一部の移植不可能なコードは、lint のデフォルトの動作によってフラグを付けられます。また、ほかにも少数の状況で、 -p または -Xc を指定して lint を起動すると、診断されることがあります。lint は ISO C 規格に一致しない言語構造を検査します。-p および -Xc のもとで発行されるメッセージに関しては、「4.6.2 lint ライブラリ」を参照してください。
例:
一部の C 言語の実装では、signed または unsigned のどちらも明示的に宣言されていない文字変数は、符号付き (signed) の量として扱われ、通常は -128 ~ 127 の範囲になります。ほかの実装では、これらは負にならない量として扱われ、通常は 0 ~ 255 の範囲になります。文字変数が負でない値を取るマシンでは、次のテスト (EOF の値が -1) は常に失敗します。
char c; c = getchar(); if (c == EOF) ...
-p 付きで呼び出された lint は、単純な char が負の値を持つ可能性があることを暗示する比較をすべて検査します。ただし、この例で c を signed char として宣言すると、問題ではなく診断が除去されます。getchar() は可能なすべての文字と独自の EOF 値を戻す必要があるため、char には値を格納できません。この例は、処理系ごとに定義される符号拡張から生ずるおそらくもっとも一般的なものですが、lint の移植性オプションを注意深く使用すると移植性に関係しないバグを発見するのに役立つ可能性があることを示しています。ここでは c を int で宣言します。
同様の問題がビットフィールドにもあります。定数値がビットフィールドに代入される場合、その値を保持するにはフィールドが小さすぎる場合があります。int 型のビットフィールドを符号なし (unsigned) の量として取り扱うマシンでは、int x:3 の範囲で許可される値が 0 ~ 7 であるのに対し、符号付き (signed) の量として取り扱うマシンでは -4 ~ 3 になります。ただし、int 型として宣言された 3 ビットのフィールドは、後者のマシンでは値 4 を保持できません。-p を指定して呼び出された lint は、unsigned int または signed int を除き、すべてのビットフィールドの型にフラグを付けます。これらのみが、移植可能なビットフィールド型です。コンパイラは、ビットフィールドの型 int、char、short、および long をサポートしますが、これらは unsigned、signed またはそのどちらでもない場合があります。さらにコンパイラは enum のビットフィールドの型もサポートします。
大きなサイズの型が小さなサイズの型に代入されると、問題が発生することがあります。有効なビットが切り捨てられると正確な値を保持できなくなります。
short s; long l; s = l;
lint は、デフォルトでこのような代入すべてを知らせます。診断は、-a オプションを指定して呼び出すことにより抑制することができます。どのオプションを指定して lint を呼び出しても、ほかの診断をも抑制する可能性があることに注意してください。2 つ以上の診断を抑制するオプションについては、「4.6.2 lint ライブラリ」にあるリストを参照してください。
あるオブジェクト型へのポインタをより厳格な整列要件を持つオブジェクト型へのポインタにキャストすると、移植性が損なわれる可能性があります。lint は次の例にフラグを付けます。なぜなら、大部分のマシンでは、char が任意のバイト境界から開始できるのに対し、int はそうできないからです。
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’)
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)) ...
lint ライブラリを使用して、呼び出したライブラリ関数とプログラムとの互換性 (関数戻り型の宣言、関数が期待する引数の数と型など) を検査できます。標準 lint ライブラリは、C 言語処理系で供給されるライブラリに対応し、一般にはシステムの標準位置であるディレクトリに格納されています。慣例では、lint ライブラリは llib-lx.ln という形の名前を持ちます。
lint 標準 C ライブラリ、llib-lc.ln は、lint コマンド行の末尾にデフォルトで追加されます。それとの互換性の検査は、-n オプションを呼び出すことで抑制すできます。そのほかの lint ライブラリは、-l に対して引数として指定することでアクセスされます。この例では、lint ライブラリ llib-lx.ln との互換性について、file1.c と file2.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 -o と cc -o は類似しています。ライブラリは、同様に関数プロトタイプ宣言のファイルから作成できます。ただし、NOTE(LINTLIBRARY) と NOTE(PROTOLIB(n)) の両方が宣言ファイルの先頭に挿入されている必要があります。n が 1 の場合、プロトタイプ宣言は古いスタイルの定義と同様にライブラリ .ln ファイルに書き込まれます。n がデフォルトの 0 の場合、処理はキャンセルされます。-y を指定して lint を呼び出しても、lint ライブラリを作成することができます。次のコマンド行の場合は、その行に指定された各ソースファイルが 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 は /usr/lib ディレクトリのライブラリを使用します。これらのライブラリは基本 lint 形式です。makefile を一度実行して新しい形式の拡張 lint ライブラリを作成すれば、拡張 lint をより効率的に利用することができます。makefile を実行して新しいライブラリを作成するには、次のコマンドを使用してください。
% cd install-directory/prod/src/lintlib; make
ここで、install-directory はインストールディレクトリです。makefile の実行後、lint は /usr/lib ディレクトリ内のライブラリの代わりに、拡張モードの新しいライブラリを使用します。
lint フィルタは、プロジェクト固有のポストプロセッサで、通常は awk スクリプトや類似のプログラムを使用して lint の出力を読み取り、プロジェクトが特に真の問題を識別していないと判断したメッセージを捨てます。たとえば、ときどきまたは常に無視される値を返す文字列関数などです。lint オプションと指令が出力に対して十分な制御が提供しないときは、lint フィルタはカスタマイズされた診断レポートを生成します。
lint の 2 つのオプションはフィルタを開発する際に特に役立ちます。
-s は、複合診断を問題診断の発生ごとに発行される単純な 1 行メッセージに変換します。この解析されたメッセージ書式は awk スクリプトによる分析に適しています。
-k オプションでは、ソースファイル内で記述した特定のコメントが出力されます。これは、プロジェクトの決定をドキュメント化する場合とポストプロセッサの動作を指定する場合の両方で役立つ可能性があります。コメントが予想される lint メッセージを示していて、報告されたメッセージがそれと同一であった場合、メッセージは除かれます。-k を使用するときは NOTE(LINTED(メッセージ)) 指令をコメントしたいコードの前の行に挿入してください。ここでのメッセージは、lint が -k を指定して呼び出されたときに出力されるコメントです。
NOTE(LINTED(メッセージ)) を含むファイルに対して -k が呼び出されないときの lint の動作については、表 4-9 を参照してください。