6    lint による C プログラムの検査

lint プログラムを使用して,C プログラムにコーディング上の問題が含まれているかどうかを確認することができます。 lint プログラムは C コンパイラよりも厳密にプログラムをチェックして,問題となりそうな点を指摘するメッセージを表示します。 メッセージのいくつかはソース・コードの修正が必要ですが,情報メッセージで修正を必要としないものもあります。

この章では,次の項目について説明します。

lint のオプションの詳細については lint(1) を参照してください。

6.1    lint コマンドの構文

lint コマンドの構文は次のとおりです。

lint [ options ] [ file ... ]

options

lint の検査を制御するオプション。

lint のオプションとして,cc ドライバ・オプション -std-std0-std1 を使用することができます。 これらのオプションは,使用する lint ライブラリの選択に影響を与えるとともに,ソースの解析に影響を与えます。 -std あるいは -std1 オプションを指定すると,lint で ANSI 解析規則が有効になります。

-MA lint オプションを使用すると,C プリプロセシングで -std1 が使用され,-D プリプロセッサ・オプションで _ABSI_C_SOURCES が定義されます。 次に示すのは,各オプションに対する lint の動作です。

lint オプション プリプロセッサ・スイッチ lint 解析 lint ライブラリ
-MA -std1 および -D_ANSI_C_SOURCE ANSI llib-lansi.ln
-std -std ANSI llib-lcstd.ln
-std1 -std1 ANSI llib-lcstd.ln
-std0 -std0

EXTD [脚注 5]

llib-lc.ln

file

lint が検査する C 言語のソース・ファイルの名前。 ファイル名には,次のいずれかの接尾語が必要です。

接尾語 説明
.c C ソース・ファイル
.i C プリプロセッサ (cpp) によって作成されるファイル
.ln lint ライブラリ・ファイル

lint ライブラリ・ファイルは,以前に lint プログラムを -c または -o オプションを指定して呼び出した結果です。 これは,cc コマンドに入力として .c ファイルを指定した場合に,.o ファイルが作成されるのと似ています。 lint プログラムに入力として lint ライブラリが指定できる機能により,大規模なアプリケーションにおけるモジュール間のインタフェース・チェックが容易になります。 lint ライブラリの構造を makefile に指定する規則を追加すると,そのようなアプリケーションをもっと効率よく作成できます。 lint ライブラリの作成方法については,6.10 節を参照してください。

-lx オプションを使用して,システムの省略時のライブラリ探索ディレクトリのいずれかにある lint ライブラリを入力として指定することもできます。 ライブラリ名は次の形式でなければなりません。

llib-llibname.ln

省略時の設定により,lint プログラムは,拡張 C (K&R C) lint ライブラリ (llib-lc.ln) を,コマンド行に指定されたファイルのリストに追加します。 -std または -std1 オプションが使用された場合は,標準 C lint ライブラリ (llib-lcstd.ln) が代わりに追加されます。

システムには次の追加ライブラリが含まれています。

ライブラリ 説明 指定方法
crses curses ライブラリ呼び出し構文を検査する。 -lcrses
m 算術ライブラリ呼び出し構文を検査する。 -lm
port 他のシステムとの移植性を検査する。 -p (-lport ではない)
ansi ANSI C 規格の規則に準拠させる。 -MA (-lansi ではない)

コマンド行にオプションを指定しなければ,lint プログラムは,指定の C ソース・ファイルを検査して,次のいずれかのコーディング上の問題が見つかった場合にはメッセージを書き込みます。

また,lint プログラムは,ソース・プログラムの文中に構文エラーがないかどうかを検査します。 構文検査は,lint コマンドに指定したオプションに関係なく常に行われます。

lint がエラーを報告しなければ,プログラムの構文に誤りがなく,正常にコンパイルされます。 ただし,検査をパスしても,プログラムが正確に動作する,またはプログラムの論理設計が正確であるということにはなりません。

ユーザ固有の lint ライブラリの作成方法については,6.10 節を参照してください。

6.2    プログラム・フロー検査

lint プログラムは,デッド・コード,すなわち到達不可能なために実行されないプログラム部分がないかどうかを検査します。 プログラムの流れを変更する,gotobreakcontinue,および return などの文の直後にあって,ラベルが付いていない文についてのメッセージを出力します。

lint プログラムは,先頭から開始できないループについても検出して,メッセージを書き込みます。 このような型のループを含んだプログラムの中には,正しい結果を出すものもありますが,問題を引き起こす可能性があります。

lint プログラムは,呼び出されても呼び出し元のプログラムに戻らない関数を認識しません。 たとえば,exit を呼び出すと到達不可能なコードを生成しますが,lint ではそのコードを検出できません。

yacclex によって生成されたプログラムには,到達不可能な break 文があります。 lint プログラムは通常,これらの各 break 文に対するエラー・メッセージを出力します。 これらの break 文に関連する余計なコードを取り除くには,プログラムをコンパイルするときに,cc コマンドで -O オプションを使用します。 yacclex の出力コードを検査するときには,lint プログラムに -b オプションを使用すると,これらのメッセージは出力されません。 yacc および lex についての情報は,『プログラミング・サポートツール・ガイド』を参照してください。

6.3    データ型検査

lint プログラムは,C 言語のデータ型の検査規則をコンパイラが行うよりも厳密に適用します。 コンパイラが行う検査のほかに,lint は,次のような点について,データ型エラーの可能性がないかどうかを検査します。

これらの問題の可能性についての詳細は,以降の各項で説明します。

6.3.1    二項演算子および暗黙の代入

C 言語では,文中に次のデータ型を混在させることができ,コンパイラは,その混在についてのエラーを示しません。

char
short
int
long
unsigned
float
double

C 言語では,上記のグループ内のデータ型を互いに自動的に変換するため,より柔軟にプログラミングできます。 ただし,この柔軟性は,C 言語自体によって保証されるものではないため,プログラマ自身が,データ型を混在させても確実に所定の結果が得られるようにする必要があります。

次の方法で使用する場合に,これらのデータ型を混在させることができます。 例では,alphachar 型で,numint 型です。

任意のデータ型の配列とその同じ型を指すポインタが混在する場合以外は,ポインタのデータ型は正確に一致しなければなりません。

6.3.2    構造体と共用体

lintプログラムは,次のような条件の場合の構造体演算を検査します。

lint プログラムは,共用体の参照についても同様の検査を行います。

6.3.3    関数定義と使用方法

lint プログラムは,関数の引数とリターン値の照合に厳密な規則を適用します。 引数とリターン値は,型が一致している必要があります。 ただし,次のような例外があります。

6.3.4    列挙値

lint プログラムは,列挙データ型の変数が次の条件を満たしているかどうかを検査します。

6.3.5    型キャスト

C 言語のプログラムでは,型キャストによって,ある型のデータを別の型のデータのように扱うことができます。 lint プログラムは,型キャストがないかどうかを検査し,もしあればメッセージを出力します。

lint コマンド行に -wp および -h オプションを指定すると,キャストに関する警告メッセージの出力を制御します。 どちらのオプションも使用しなければ,lint は移植性について問題が生じる可能性があるキャストに関して,警告メッセージを生成します。

移行検査モードでは,-Qc でキャストの警告メッセージの出力が抑制されます(6.6 節を参照)。

6.4    変数および関数の検査

lint プログラムは,プログラム内で宣言されていても使用されていない変数がないかどうかを検査します。 また,変数および関数の使用方法について次のエラーがないかどうかを検査します。

これらの問題の可能性について,以降の各項で説明します。

6.4.1    矛盾する値を返す関数

関数が,ある条件がそろっているときにしか値を返さない場合,プログラムの結果は予測することができません。 lint プログラムは,関数がこのような動作をしないかどうかを検査します。 たとえば,次の 2 つの文が 1 つの関数定義にある場合には,その関数を呼び出すプログラムは,リターン値を受け取ったり,受け取らなかったりします。

return(expr);
 

.
.
.
return;  

これらの文によって,lint プログラムは,次のようなメッセージを出力し,問題の可能性を指摘します。

function name has return(e); and return
 

lint プログラムはまた,関数コードの終端に到達したために発生するリターン (暗黙のリターン) がないかどうかについて,関数を調べます。 たとえば,次は関数の一部ですが,a が偽と判断された場合は,checkoutfix_it を呼び出し,リターン値なしで戻ります。

checkout (a)
{
        if (a) return (3);
        fix_it ();
}
 

これらの文に対して,lint プログラムは,次のメッセージを出力します。

function checkout has return(e); and return
 

fix_it が,exit と同様に戻らない場合,lint は,エラーがなくてもメッセージを出力します。

6.4.2    使用されていない関数値

lint プログラムは,関数が値を返しても,呼び出しているプログラムがその値を使用していない場合がないかどうかを検査します。 値が使用されていない場合には,関数定義が無効である可能性があるため,調べて,変更するかまたは削除するかを決定します。 値が使用されることがある場合は,関数は,呼び出し側プログラムが検査をしていないという内容のエラー・コードを戻します。

6.4.3    関数についての検査の禁止

lint が,関数についての問題を検査しないようにするには,lint コマンドに次の表にあるオプションを 1 つまたは複数指定します。

-x extern 文で宣言されているが,使用されていない変数を検査しない。
-v レジスタ引数としても宣言されているものを除き,使用されていない関数の引数を検査しない。
-u 使用されているのに定義されていない,または定義されているのに使用されていない関数および外部変数を検査しない。 大きなプログラムのファイルのサブセットで lint を実行する場合にこのオプションを使用すると,無駄なメッセージが削除される。 一緒に作動する一部の (全部ではない) ファイルで lint を使用すると,それらのファイルで定義されている関数および変数は,ほとんど使用されない。 また,その他のファイルで定義されている多くの関数および変数が使用される。

プログラムに指示文を記述しても,チェックを制御することができます。

6.5    初期化前の変数使用のチェック

lint プログラムは,ローカル変数 (autoregister の記憶クラス) が,値を割り当てられていないのに使用されていないかどうかを検査します。 auto (自動) 記憶クラスまたは register 記憶クラスで変数を使用するには,変数のアドレスも取得することが必要です。 これは,プログラムが変数のアドレスを認識すれば,いつでもそのアドレスによって変数を使用できるためです。 したがって,プログラムが,変数に値を割り当てていないにもかかわらず変数のアドレスを見つけようとした場合,lint はエラーを報告します。 lint はファイル内での変数の物理的な順番と使用されているかどうかを検査するだけであるため,適切に初期化された変数についてのメッセージを実行順に出力することがあります。

lint プログラムは,次の項目を認識してメッセージを出力します。

注意

Tru64 UNIX オペレーティング・システムは,static 変数と extern 変数をゼロに初期化します。 したがって,lint はプログラムの開始時にこれらの変数が 0 に設定されていると見なし,その変数にすでに値が割り当てられているかどうかについては,変数の使用時には確認しません。 このような初期化を行っていないシステムのプログラムを開発する場合は,プログラムが static 変数と extern 変数を初期値に設定していることを確認してください。

6.6    移行検査

lint を使用して,32 ビット・オペレーティング・システムから Tru64 UNIX オペレーティング・システムに移行する場合に,問題が発生する可能性のあるすべての一般的なプログラミング技法を検査します。 -Q オプションは,64 ビット・システムに移行する ULTRIX および DEC OSF/1 バージョン 1.0 プログラムのチェックをサポートします。

-Q オプションを指定すると,その他のプログラム上の問題の検査ができなくなります。 このため,移行検査の目的だけで使用してください。 サブオプションは,特定のカテゴリの検査を抑止するために使用できます。 たとえば,-Qa と入力すると,ポインタの位置合わせに関する問題を検査しません。 -Q オプションには複数のサブオプションを指定することができます。 たとえば -QacP は,ポインタ位置合わせの問題,問題のある型キャスト,および関数プロトタイプの検査をそれぞれ抑止します。 移行検査についての詳細は, lint(1) を参照してください。

6.7    移植性検査

lint を使用すると,異なる C 言語コンパイラや他のシステムを使用している C プログラムのコンパイルおよび実行が確実にできるようになります。

以降の各項で,他のシステム上でプログラムをコンパイルする前に確認すべき点を示します。 ただし,これらの点だけを確認すれば,どのシステムでもプログラムが実行可能であるというわけではありません。

注意

llib-port.ln ライブラリは,-lport オプションではなく,-p オプションを使用すると,取り込むことができます。

6.7.1    文字

システムの中には,C 言語プログラムで使用する文字を,--128 以上 127 以下の符号付きの数として定義しているものもあります。 その他のシステムでは文字を正の値で定義しています。 lint プログラムは,他のシステムに移植できない可能性のある文字の比較または文字の代入がないかどうかを検査します。 たとえば,次のコードの一部は,あるシステムでは動作しますが,文字が常に正の値を取るシステムでは動作しません。

char c;
 

.
.
.
if( ( c = getchar() ) <0 )...  

この文により,lint プログラムは,次のようなメッセージを出力します。

nonportable character comparison
 

文字を正の値で定義しているシステムでプログラムを動作させるには,getchar が整数値を返すため,c を整数として宣言します。

6.7.2    ビット・フィールド

プログラムを別のシステムに移植する際,ビット・フィールドに問題がある可能性があります。 新しいシステムではビット・フィールドも符号付き数である可能性もあります。 したがって,ビット・フィールドに定数値を代入する場合は,フィールドが小さすぎて値を保持できない可能性があります。 すべてのシステムで定数値の代入ができるようにするには,定数値を設定する前に,ビット・フィールドを unsigned 型として宣言します。

6.7.3    外部名サイズ

あるタイプのシステムから別のタイプのシステムに変更する場合は,プロセスのロード時に得られる外部名についての情報に,次のような相違があることに注意してください。

あるシステムから別のシステムに移植する場合は,プログラムのロード時の問題を避けるため,常に次の手順を行ってください。

  1. 各システムの条件を調べる。

  2. -p オプションを付けて lint を実行する。

-p オプションを付けると,lint は,すべての外部シンボルを小文字に変更し,入力ファイルの検査時には 6 文字以内に制限します。 出力されたメッセージには,変更が必要な用語が示されます。

6.7.4    複雑な式の使用と副作用

複雑な式を使用する場合には,次の点に注意してください。

次に,これらの相違が原因で起こる 3 種類の問題の例を示します。

6.8    コーディング・エラーおよびコーディングのスタイルの相違のチェック

lint は,発生し得るコーディング・エラーを検出したり,lint が予期しているコーディング・スタイルとの相違を検出したりします。 コーディング・スタイルは主として個人の趣味の問題ですが,相違する点は,正しくかつ必要なものであることを確認してください。 以降の各項で,lint が検出するコーディング・エラーおよびコーディング・スタイルについての問題を示します。

6.8.1    long 型変数の int 型変数への代入

long 型の変数を int 型の変数に代入すると,プログラムは正しく動作しません。 long 型変数は,int 型変数のスペースに適合するように切り捨てられるため,データが失われる可能性があります。

typedef を使用するプログラムを変換して異なるシステム上で実行すると,この種のエラーが頻繁に発生します。

long 変数の int 変数への代入を検出した場合,lint がメッセージを書き込まないようにするには,-a オプションを使用します。

6.8.2    演算子の優先度

lint プログラムは,演算子の優先度に関するエラーを検出します。 複雑なシーケンス内では順番を表すカッコがなければ,このエラーの検出は困難です。 たとえば,次の文では優先度がはっきりしていません。

if(x&077==0). . .   /* if(x & (077 == 0))と評価される。*/
                    /* 本来はif((x & 077) == 0)である。*/
x<<2+40             /* x <<(2+40) と評価される。*/
                    /* 本来は (x<<2) + 40 である。*/
                    /* x を左へ 42 文字だけシフトする。*/
 

カッコを使用して演算がより明解になるようにしてください。 カッコを使用しない場合には,lint はメッセージを出力します。

6.8.3    宣言の矛盾

lint プログラムは,内部ブロックで宣言される変数が,外部ブロックでの宣言と矛盾するような場合に,メッセージを出力します。 この変数を実行することはできますが,プログラムに問題が起こる可能性があります。

lint プログラムに -h オプションを付けると,lint は,宣言の矛盾がないかどうかを検査しません。

6.9    テーブル・サイズの増加

lint コマンドの -N オプションおよび関連のサブオプションは,さまざまな内部テーブルのサイズが,省略時の値ではプログラムを動作させるのに不十分な場合,実行時に増加させます。 内部テーブルには次のようなものがあります。

これらのテーブルは,lint プログラムによって動的に割り当てられます。 大きなソース・ファイルについて -N オプションを使用すると,性能を向上させることができます。

6.10    lint ライブラリの作成

システム・ライブラリ・ルーチン以外のライブラリ・ルーチンを作成するプログラム開発プロジェクトでは,さらに lint ライブラリを作成して,プログラムの構文を検査することができます。 lint プログラムは,lint ライブラリを使用して,C 言語の標準関数だけでなく,新しい関数を検査することができます。 新しい lint ライブラリを作成するには,次の手順を実行してください。

  1. 新しい関数群を定義する 1 つの入力ファイルを作成する。

  2. 1. の入力ファイルを処理して lint ライブラリ・ファイルを作成する。

  3. 2. で作成した lint ライブラリを使用して,lint を実行する。

以降の各項で,これらの手順についての詳細を説明します。

6.10.1    入力ファイルの作成

次の例は,lint に検査させる関数を 3 つ定義した入力ファイルです。

/*LINTLIBRARY*/
 
#include <dms.h>
 
int  dmsadd( rmsdes, recbuf, reclen )
               int rmsdes;
               char *recbuf;
               unsigned reclen;
             { return 0; }
int dmsclos( rmsdes)
               int rmsdes;
             { return 0; }
int dmscrea(  path, mode, recfm, reclen )
               char *path;
               int mode;
               int recfm;
               unsigned reclen;
             { return 0; }
 

入力ファイルとは,エディタで作成するテキスト・ファイルのことで,次のものから構成されます。

もう 1 つの方法として,関数プロトタイプから lint ライブラリ・ファイルを作成することができます。 たとえば,dms.h ファイルに次のプロトタイプが含まれているとします。

int dmsadd(int,
           char*,
           unsigned);
int dmsclose(int);
int dmscrea(char*,
            int,
            int,
            unsigned);
 

この場合,入力ファイルには次の記述が含まれます。

/*LINTSTDLIB*/
#include <dms.h>
 

ヘッダ・ファイルに別のヘッダ・ファイルが含まれている場合は,LINTSTDLIB コマンドは特定のファイルに制限されます。

/*LINTSTDLIB_dms.h*/
 

この場合は,dms.h で宣言されたプロトタイプのみが展開されます。 LINTSTDLIB コマンドは複数記述することができます。

いずれの場合にも,入力ファイルの名前には,接頭語 llib-l がなければなりません。 たとえば,この項で作成されるサンプルの入力ファイルの名前は,llib-ldms となります。 ファイルに名前を付ける場合は,/usr/ccs/lib ディレクトリにある既存のファイルと同じ名前でないことを確認してください。

6.10.2    lint ライブラリ・ファイルの作成

次のコマンドは,前項で説明した入力ファイルから lint ライブラリ・ファイルを作成します。

% lint [options] -c llib_ldms.c
 

このコマンドは,入力ファイルとして llib-ldms.c を使用して,lint ライブラリ・ファイル llib-ldms.ln を作成するように lint に指示しています。 その後 llib-ldms.ln をシステム lint ライブラリ (つまり,lint コマンドの -lx オプションで指定するライブラリ) として使用するには,それを /usr/ccs/lib に移動します。 ANSI 前処理規則を使用してライブラリを構築するには,-std または -std1 オプションを使用します。

6.10.3    新しいライブラリによるプログラムの検査

新しいライブラリを使用してプログラムを検査するには,次の形式で lint コマンドを使用します。

lint -lpgm filename.c
        

pgm 変数にはライブラリの識別子を,filename.c 変数には,検査する C 言語のソース・コードの入っているファイル名を指定します。 他のオプションが指定されていない場合,lint プログラムは,指定された特定の lint ライブラリだけでなく,標準の lint ライブラリとも照合して,C 言語ソース・コードを検査します。

6.11    lint エラー・メッセージ

lint のエラー・メッセージのほとんどはわかりやすいものですが,説明を加えなければ誤解が生じやすいメッセージもあります。 通常,メッセージの意味を理解すると,エラーの修正は簡単です。 次に,意味のあいまいな lint メッセージを示します。

constant argument to NOT

定数が NOT 演算子 (!) と一緒に使用されています。

これは一般的に行われるコーディングであり,必ずしも問題を示しているわけではありません。 次のようなコーディングをすると,このメッセージが出力されることがあります。

% cat x.c
#include <stdio.h>
#define SUCCESS    0
 
main()
{
	  int value = !SUCCESS;
 
	  printf("value = %d\n", value);
	  return 0;
}
% lint -u x.c
"x.c", line 7: warning: constant argument to NOT
% ./x
value = 1
% 

lint はメッセージを出力しますが,プログラムは期待どおりに実行されます。

対応策: -wC オプションを使用して,lint の警告メッセージを出力しないようにします。

constant in conditional context

条件を指定すべきところに定数が使用されています。

この問題は,マクロの使用方法によって,ソース・コードでよく起こります。 たとえば,次のような場合です。

typedef struct _dummy_q {
  int lock;
  struct _dummy_q *head, *tail;
} DUMMY_Q;
 
#define QWAIT   1
#define QNOWAIT 0
#define DEQUEUE(q, elt, wait)    [1]         \
        for (;;) {                           
            simple_lock(&(q)->lock);         
        if (queue_empty(&(q)->head))         
            if (wait) {          [1]         \
                assert(q);                   
                simple_unlock(&(q)->lock);   
                continue;                    
            } else                           
                *(elt) = 0;                  
        else                                 
                  dequeue_head(&(q)->head);  
                  simple_unlock(&(q)->lock); 
        break;                               
    }
 
int doit(DUMMY_Q *q, int *elt)
{
  DEQUEUE(q, elt, QNOWAIT);
}

  1. QWAIT または QNOWAIT オプションは 3 番目の引数 (wait) として渡され,その後 if 文で使用されます。 コードは正しいのですが,lint は警告メッセージを出力します。 これは,通常このように使用される定数は不必要であり,無駄な命令を生成するためです。 [例に戻る]

対応策: -wC オプションを使用して,lint の警告メッセージを出力しないようにします。

conversion from long may lose accuracy

  • 符号付き long が小さい要素 (int など) にコピーされています。 このメッセージは必ずしも誤解を招くものではありませんが,よく起こり,次の例に示すように,コーディングに問題がある場合もあれば,問題がない場合もあります。

    long BuffLim = 512;         [1]
     
    void foo (buffer, size)
    char *buffer;
    int size;
    {
    register int count;
    register int limit = size < (int)BufLimit ? size : (int)BufLim;  [1]
     
    

    1. 適切な (int) キャストが使用されていますが,lint プログラムは変換エラーを報告します。 [例に戻る]

    対応策: lint がメッセージを報告するコード・セクションを調べるか,または -wC オプションを使用して,lint の警告メッセージを出力しないようにします。
  • declaration is missing declarator

  • プログラムの宣言節にセミコロン (;) だけの行があります。

    このようなコードを書かなくても,マクロの後ろにセミコロンを記述した場合などに,このようなコードが生成されることがあります。 条件化により,マクロが空として定義された場合,このメッセージが出力されることがあります。

    対応策: 後ろのセミコロンを削除してください。

  • degenerate unsigned comparison

  • 結果がゼロより小さくなると思われる場合に,符号なし比較が符号付きの値に対して実行されました。

    次のようなプログラムでこのメッセージが出力されます。

    % cat x.c
    #include <stdio.h>
    unsigned long offset = -1;
     
    main()
    {
        if (offset < 0) {               [1]
            puts ("code is Ok...");
            return 0;
        } else {
            puts ("unsigned comparison failed...");
            return 1;
        }
    }
    % cc -g -o x x.c
    % lint x.c
    "x.c" line 7: warning: degenerate unsigned comparison
    % ./x
    unsigned comparison failed...
    % 
    

    1. このような符号なし比較は,符号なし変数に負の値が入っている場合には失敗します。 符号付き比較を意図していたかどうかによって,結果のコードは正しいこともあります。 [例に戻る]

    対応策: この例は,次の 2 つの方法で修正することができます。

    • if 比較の offset の前に (long) キャストを追加する。

    • offset の宣言を unsigned  long から long に変更する。

    場合によっては,符号付きの値を符号なしにキャストする必要があります。

  • function prototype not in scope

  • このエラーは厳密には関数プロトタイプに関連していません。 実際,このエラーは前もって宣言や定義を行っていない関数を呼び出した場合に起こります。

    対応策: 関数プロトタイプ宣言を追加してください。

  • null effect

  • lint プログラムが何も行わないキャストまたは文を検出しました。

    次のコードは,lint がこのメッセージを出力するさまざまなコーディング例を示しています。

        scsi_slot = device->ctlr_hd->slot,unit_str;     [1]
     
        #define MCLUNREF(p)             \
                (MCLMAPPED(p) && --mclrefcnt[mtocl(p)] == 0)
     
        (void) MCLUNREF(m);                             [2]
     
     
    

    1. 理由: unit_str は何も行いません。 [例に戻る]

    2. 理由: MCLUNREF はマクロであるため (void) は不要です。 [例に戻る]

    対応策: 不必要なキャストや文を削除したり,マクロを修正してください。
  • possible pointer alignment problem

  • 位置合わせの問題が生じるような方法でポインタが使用されています。

    次のコードは,lint によりこのメッセージが生成されるコード例を示しています。

    read(p, args, retval)
            struct proc *p;
            void *args;
            long *retval;
    {
            register struct args {
                    long    fdes;
                    char    *cbuf;
                    unsigned long  count;
            } *uap = (struct args *) args;          [1]
            struct uio auio;
            struct iovec aiov;
    

    1. *uap  =  (struct  args  *)  args という文が,このエラーを引き起こします。 この構文は有効であり,カーネル・ソース内で使用されるため,このメッセージはフィルタされます。 [例に戻る]

  • precision lost in field assignment

  • ビット・フィールドがある値を保持するには小さすぎる場合に,そのフィールドにその定数を代入しようとしました。

    次のコードはこの問題を示しています。

    % cat x.c
    struct bitfield {
        unsigned int block_len : 4;
    } bt;
     
    void
    test()
    {
        bt.block_len = 0xff;
    }
    % lint -u x.c
    "x.c", line 8: warning: precision lost in field assignment
    % cc -c -o x x.c
    %
    

    このコードはエラーを生じることなくコンパイルされます。 ただし,ビット・フィールドがその定数を保持するには小さすぎるため,意図した結果にならず,実行時エラーが生じる場合もあります。

    対応策: ビット・フィールドのサイズを変更するか,または異なる定数値を代入してください。

  • unsigned comparison with 0

  • 結果がゼロに等しいか大きいと予想される場合に,ゼロに対して符号なしの比較が実行されました。

    次のプログラムは,このような状況を示しています。

    % cat z.c
    #include <stdio.h>
    unsigned offset = -1;
     
    main()
    {
        if (offset > 0) {               [1]
            puts("unsigned comparison with 0 Failed");
            return 1;
        } else {
            puts("unsigned comparison with 0 is Ok");
            return 0;
        }
    }
    % cc -o z z.c
    % lint z.c
    "z.c", line 7: warning: unsigned comparison with 0?
    % ./z
    unsigned comparison with 0 Failed
    % 
    

    1. このような符号なし比較は,符号なし変数に負の値が入っている場合に失敗します。 符号付き比較を意図していたかどうかによって,結果のコードは正しくない場合があります。 [例に戻る]

    対応策: この例は,次の 2 つの方法で修正できます。

    • if 比較の offset の前に,(int) キャストを追加する。

    • offset の宣言を unsigned から int へ変更する。

  • 6.12    警告クラス・オプションを使用した lint メッセージの抑制

    lint プログラムにいくつかの lint 警告クラスを追加することによって,条件処理で使用される定数,移植性,およびプロトタイプの検査に関連したメッセージの出力を抑制できるようになりました。 lint コマンドで警告クラス・オプションを使用すると,どの警告クラスのメッセージでも抑制することができます。

    警告クラス・オプションのフォーマットは次のとおりです。

    -wclass [ class... ]

    省略時の設定ではすべての警告クラスがアクティブですが,class 引数として適切なオプションを指定すると,個々のクラスを非アクティブにすることができます。 表 6-1 で個々のオプションについて説明します。

    注意

    lint メッセージには,複数の警告クラスに依存するものがいくつかあります。 したがって,これらのメッセージを抑制するには,複数の警告クラスを指定する必要があります。 表 6-1 の脚注は,複数の警告クラスを指定して,どのメッセージが抑制できるかだけを示しています。

    たとえば,条件式の定数に関連する lint メッセージは,(6.11 節で説明しているように) 必ずしもコーディング上の問題を示していないので,-wC オプションを使用してメッセージを抑制するとします。 -wC オプションは,次のメッセージの出力を抑制します。

    移植性の検査に関するメッセージの多くは,非 ANSI コンパイラおよび Tru64 UNIX 用の C コンパイラに存在しない制限に関連するため,-wp オプションを使用して,メッセージを抑制することができます。 -wp オプションは,次のメッセージの出力を抑制します。

    関数プロトタイプの使用は (6.13 節で説明しているように) 好ましいコーディング方法ですが,多くのプログラムでは使用されていません。 -wP オプションを使用すると,プロトタイプの検査を禁止することができます。 -wP オプションは,次のメッセージの出力を抑制します。

    表 6-1:  lint 警告クラス

    警告クラス クラスの説明
    a

    非 ANSI 機能。 次のメッセージの出力を抑制する。

    c

    符号なし値の比較。 次のメッセージの出力を抑制する。

    • Comparison of unsigned with negative constant

    • Degenerate unsigned comparison

    • Possible unsigned comparison with 0

    d

    宣言の一貫性。 次のメッセージの出力を抑制する。

    • External symbol type clash for %s

    • Illegal member use: perhaps %s.%s [脚注 7]

    • Incomplete type for %s has already been completed

    • Redeclaration of %s

    • Struct/union %s never defined [脚注 7]

    • %s redefinition hides earlier one [脚注 6] [脚注 7]

    h

    ヒューリスティックな障害。 次のメッセージの出力を抑制する。

    k

    K & R 型のコードを期待する。 次のメッセージの出力を抑制する。

    l

    非 long 型変数に long 型の値を代入した。 次のメッセージの出力を抑制する。

    • Conversion from long may lose accuracy

    • Conversion to long may sign-extend incorrectly

    n

    空作用コード。 次のメッセージの出力を抑制する。

    o

    未知の評価順序。 次のメッセージの出力を抑制する。

    • Precedence confusion possible: parenthesize! [脚注 7]

    • %s evaluation order undefined

    p

    移植性に関連するさまざまな事柄。 次のメッセージの出力を抑制する。

    • Ambiguous assignment for non-ANSI compilers

    • Illegal cast in a constant expression

    • Long in case or switch statement may be truncated in non-ANSI compilers

    • Nonportable character comparison

    • Possible pointer alignment problem, op %s [脚注 7]

    • Precision lost in assignment to (possibly) sign-extended field

    • Precision lost in field assignment

    • Too many characters in character constant

    r

    Return 文の一貫性。 次のメッセージの出力を抑制する。

    • Function %s has return(e); and return;

    • Function %s must return a value

    • main() returns random value to invocation environment

    S

    記憶容量の検査。 次のメッセージの出力を抑制する。

    • Array not large enough to store terminating null

    • Constant value (0x%x) exceeds (0x%x)

    u

    変数および関数の適切な使用。 次のメッセージの出力を抑制する。

    A

    すべての警告クラスをアクティブにする。 lint スクリプトにおける省略時のオプション。 別の A クラスを指定すると,全クラスの設定が切り替わる。

    C

    条件処理で使用されている定数。 次のメッセージの出力を抑制する。

    D

    外部宣言が使用されていない。 次のメッセージの出力を抑制する。

    • Static %s %s unused

    O

    使用されていない機能。 次のメッセージの出力を抑制する。

    • Storage class not the first type specifier

    P

    プロトタイプの検査。 次のメッセージの出力を抑制する。

    • Function prototype not in scope [脚注 6]

    • Mismatched type in function argument

    • Mix of old- and new-style function declaration

    • Old-style argument declaration [脚注 6]

    • Use of old-style function definition in presence of prototype

    R

    実行されないコードの検出。 次のメッセージの出力を抑制する。

    • Statement not reached

    6.13    コンパイル時に検出される構文エラーのための関数プロトタイプの生成

    lint プログラムが報告するさまざまなエラーの修正に加えて,外部関数および静的関数の両方に対して関数プロトタイプを追加することをお勧めします。 この宣言は,コンパイラが検査する必要のある引数およびリターン値の情報を提供します。

    cc コンパイラには,自動的にプロトタイプ宣言を生成するオプションがあります。 コンパイル時に -proto[is] オプションを指定すると,関数プロトタイプを含む出力ファイル (入力ファイルと同じファイル名でファイル・タイプが .H) を作成することができます。 i オプションを指定するとプロトタイプに識別子を含み,s オプションを指定すると静的関数に対してもプロトタイプを生成します。

    .H ファイルから関数プロトタイプをコピーしてソース内の適切な場所に挿入し,ファイルを取り込みます。