% cat 実彳」 4 15 } ーⅡ sum . C 1 main (argc , argv) 2 3 5 6 7 8 9 10 11 12 13 14 % lint int char sum . C argc ; int int first 1 , sum = 0 ; first , 1ast ; atoi(* 十十 argv) ; 1ast = atoi(* 十十 argv) ; for (i first ; i く = last; (void)printf ("%d\n" , sum) ; exit ( 0 ) ; sum. c ( 2 ) : warnlng : argument argc unused in function main sum. C ( 15 ) : warning: main() returns random value tO invocation environment 11 土 b ー lc ( 237 ) s Ⅷ . c ( 14 ) exit value declared inconsistently 型が一致しないことを示しています。ここで出力されて いる llib-lc というファイノレは、 libc. a ( デフォノレ トでプログラムに読み込まれるライプラリ ) の中身を、 lint コマンドで検査するために作られた関数の入出力定 義ファイルです 3 。 こで使っている sum. c というフ ログラムでは、 exit 関数に対してなんの宣言もおこなっ ていないため、 C のコンパイラ ( もちろん lint コマン ドも ) はこの戻り値を int 型とみなして処理をおこない ます。そのため、 exit 関数に対して戻り値の型が一致 しないというメッセージが出力されたのです。 man コ マンドを使って調べてみると分かるように 4 、戻り値は void 型 ( 値を返さない関数です ) と定義されています。 対処方法はもうお分かりですね。 exit 関数の戻り値 が void 型であることを lint コマンドに知らせてやれ はいいのです。実際には、プログラムの先頭 ( もしくは exit 関数を使う前 ) に、その戻り値が分かるように宣言 を挿入します。この結果を実行例 5 に示します。 ちなみに、テストしているマシンでは、 stdlib . h と いうへッダファイルのなかに exit 関数の宣言があり、 そこで exit 関数は int 型を返すものとして宣言されて います。このヘッダファイルをインクルードしている場 合にはお手上げですね。普通はほとんど間題になること 3 このファイルに関しては、機会を改めて紹介する予定です。 4 man exit とすると、 csh の糸虧ムみコマンドの exit カ咄力されてし まいます。 exit ライプラリ関数のマニュアルは、 man 3 exit とす 150 ると見られます。 もない、ヘッダファイルのなかで定義された exit 関数 の戻り値についてシステムの誤りを検出できるなんて、 lint コマンドはすごいと思いませんか ? UNIX MAGAZINE 1993.6 ・ C 言語を拡張して lint コマンドに伝えるための構文 伝える方法には、 lint コマンドに対して、プログラムに現れない情報を ければなりません。 プログラマー自身がそのことを lint コマンドに伝えな ため、 exit 関数などの戻らない関数を使ったときには、 きすに、間違った結果を出力してしまうはずです。その 数にするとどうなるでしよう ? lint は正しい判断がで プログラマーが exit 関数を定義しなおして値を返す関 ンドが exit 関数を戻らない関数として処理していたら、 かってくれてもよさそうだと考えがちです。 lint コマ 数であることは知りません。このくらいのことは、分 ますが、 exit 関数がけして戻らない ( 戻り値のない ) 関 lint コマンドは C 言語のさまざまなことを知ってい 残ったままです。 行例 5 の出力では s . c の 17 行目に関する出力 ) は は、 exit 関数の呼出しを加えてもそのメッセージ ( 実 値に対して警告が表示されたためでした。しかし実際に exit 関数を s . c に加えたのは、 main 関数の戻り exit は戻らない
IJN Ⅸ流プログラ ミング 32 % cat 1 2 実彳」 5 17 } —n sum . C extern VOid 3 main(argc , argv) 4 5 7 8 9 10 11 12 13 14 15 16 % lint int char sum . C argc ; int int first exit ( ) ; at0i (* + + argv) ; first , 1ast ; i , sum = 0 ; 1ast = atoi(* 十十 argv) ; for (i first ; i く = last; (void)printf("%d\n" , sum) ; exit( の ; sum. c ( 4 ) : warning : argument argc unused in function main su.m. C ( 17 ) : : main() returns random value tO invocation environment を増やす ・ lint コマンドだけが処理する部分を # ifdef などで括 る ・コメントを使いその内容も検査させる などが考えられます。実際、 lint コマンドは最後の方 法を用いています。もし最初の方法を実現するならは、 C 言語のソースファイルを処理するすべてのプログラ ムを変更しなければなりません。 2 番目の方法は現実 的だとは思うのですが、 #ifdef で括る以 - E 、コンパイ ルするソースファイルと lint コマンドで処理するソー スファイルが異なるものとなってしまいます。最後の 方法を用いると、 C のプリプロセッサが # ⅲ clude や #define などの処理はおこなうが、コメントを削除す る処理はしないようにしておけば (じつは lint コマン ドは C のプリプロセッサを使っています ) 、はかの多 くのプログラムに変更を加える必要はなくなります。 lint コマンドに対して、プログラムに埋め込むことの できない情報を伝えるには、知らせたい位置に大文字で コメントを書いて指定します。この場合、知らせたい情 報は独立した単語として ( つまり前後には空白文字を入 れて ) 指定します 5 。今回は exit 関数カ唳らないことを 5 テストしたマシンでは、コメントを日本語とつなげても正しく動きま した。ただし、すべてのマシンで正常に動くとはかぎらないので注意 してください。 UNIX MAGAZINE 1993.6 示すために、 exit 関数の直後に / * NOTREACHED * / と いうコメントを加えます。これは、コメントカ甘旨定され た位置には、実行の制御がこないということを示すメッ セージです。この処理をおこなうと、実行例 6 のよう になります。 これで、 main 関数の戻り値に関する警告を消すこと ができました。 exit 関数カ唳らないことを指定したた めに、関数の最後まで実行がおこなわれて未定義の値 が返される場合を考慮しなくてよくなり、 main 関数の 戻り値に関するエラーか消えています。 本来ならば exit 関数の戻り値の型として、けして戻 らないことを示す値を使うべきなのでしようが、 C 言語 にはそのような型は定義されていません。 ANSI C で もそういう型は定義されていないようです。残念なが ら、 lint とうまくつき合っていくためには、このコメン トとは末長くお友だちでいなけれはならないようです。 使われていない引数 最後に 1 つだけ残っている警告は、 main 関数の引数 である argc が使われていないことに関するものです。 このプログラムは引数を 2 っとり、最初の引数から 2 つ目の引数までの値を加えた結果を出力するものです。 本来、引数が 2 っとも指定されていることや指定され た引数がそれぞれ整数値であること、最初の引数より 151
実彳列 2 14 } % cat —n sum . C 1 main(argc , argv) 2 3 5 6 7 8 9 10 11 12 13 % lint int char sum . C argc ; first int int i , sum = 0 ; first , 1ast; atoi (* + + argv) ; 1ast = atoi(* 十十 argv) ; for (i first; i く = 1ast; printf ("%d\n" , sum) ; sum. c ( 2 ) : warning : argument argc unused in function main sum. C ( 14 ) : warning : main ( ) tO printf returns value which is always ignored れは、 sum. 9 ( 11 ) : warning : sum may be used というものです。これは、ファイル sum. c の 11 行目 で変数 s Ⅷを使っているが、この変数にはまだ値が代 入されていないことを示しています。 このファイルの 11 行目は、 before set たが、 main 関数は int 型の値を返し、その値カワ。ログ ているというものです。 1992 年 8 月号でも説明しまし 次に注目する警告は、 main 関数が未定義の値を返し main 関数の戻り値 を設定します。 として 0 を設定しました。乗算演算子の場合、普通は 1 加算演算子を使って合計を計算しているので、初期値 しながら値を更新するときによくみられます。例では このような誤りは、ループのなかで以前の値を参照 化をおこなって、この警告を回避します ( 実行例 2 ) 。 す。今回は、 5 行目の変数定義をしているところで初期 変数の定義部分で初期化をおこなうことで回避できま れは、プログラムの先頭でこの変数に値を設定したり、 られる変数 s Ⅷの値が正しくないのも当り前です。 変数に値は代入されていません。これでは結果として得 となっていて、プログラムを見るとたしかにこれ以前に 148 ラムを起動した環境に終了コードとして渡されます。と ころがこのプログラムでは、 main 関数の最後に実行の 制御が到達するとそのまま関数を抜けてしまうために main 関数の戻り値は未定義のままとなっています。 れを回避するためには、 main 関数の最後に return 文 を加えるか、明示的に exit 関数を呼び出す方法があり ます。 こでは exit 関数を使ってみましよう ( 実行例 3 ) 。 exit 関数に渡す値は、プログラムが正常終了したこと を示す 0 を使います 2 。 今度は、さきほどのようにはうまくいきませんでし た。行番号は 15 に変わっていますが、やはり main 関 数の戻り値に関する警告が出力されています。それどこ ろか、 exit 関数についての新たなメッセージも増えてい ます。これではなんのために exit 関数の呼出しを加え たのか分かりませんね。でも安心してください。 lint コ マンドにとってはエラーか増えていますが、プログラム の意味的なエラーは減っているのです。あとは文句を言 われないように、 lint コマンドに必要なことを教えれば いいのです。 この誤りは、多くのプログラムで見受けられます。ふ つうこれらは問題にはならないのですが、このようなプ ログラムをシェル・スクリプトや Makefile のなかで使 うと問題が起こります。プログラムの終了コードはプ 2 ANSI C では、プログラムの正常終了を示す EXIT-SUCCESS という値が使えます。 UNIX MAGAZINE 1993.6
IJN Ⅸ流プログラ ミング 32 % cat 実彳デ列 3 15 } —n sum . C 1 main(argc , argv) 2 3 5 6 7 8 9 10 11 12 13 14 % lint int char sum . C argc ; **argv, int i nt i , sum = 0 ; first, last; first = atOi (* 十 + argv) ; last = atoi(* + + argv) ; fO て (i = first; i く = 1ast; S ・ u.m 十 = i ; printf ("%d\n" , sum) ; exit(0); sum. c ( 2 ) : warning : argument argc unused in function maxn sum. C ( 15 ) : : main ( ) returns random t0 invocation environment llib ー lc ( 237 ) exit value declared inconsistently s . c ( 14 ) printf returns value which is always lgnored ログラムが正常終了したかどうかを示すものとして扱 われるので、たとえば、このプログラムを Makefile の なかなどで使用すると、異常終了したと判断されて以降 のコマンドが実行されなくなってしまいます。 main 関 数の最後に、かならす return 文か exit 関数の呼出し 未使用の戻り値 を書くようにしましよう。 UNIX MAGAZINE 1993.6 て名前の置換えだけをおこなうマクロは、どうも好きに ガ去もあります。しかし、引数を付けて使う関数に対し などとしておき、 pr ⅲ tf の代わりに my-printf を使う #define my-printf (void)printf トします。マクロの機能を使って、 せん。そのためには、関数の戻り値を v 。 id 型にキャス 戻り値を使わないことを知らせてやらなければなりま プログラマーは、 lint コマンドに対して printf 関数の す。しかし、通常はこの戻り値を使用しません。そこで います。前述したように、 printf 関数は戻り値を返しま ( 1 度しか使っていませんが ) 無視されていると言って このメッセージは、 printf 関数の戻り値が、すべて に関するメッセージに注目してみましよう。 を消したいのはやまやまですが、その前に printf 関数 exit 関数を加えたために増えてしまったメッセージ なれません。普通に関数として使うだけならとくに間題 はないのですが、関数をほかの関数の引数とする場合に はどうでしよう。関数の引数として printf の代わりに my-printf を指定すると、式の値 ( つまり関数のアド レス自身 ) が void 型にキャストされてしまい、おかし なことになってしまいます。やはり関数を使うたびに void にキャストしたほうが分かりやすいのではないで しようか。 printf の戻り値が必要ないことを示すために void 型 にキャストすると、プログラムは実行例 4 のようにな ります。実際に printf 関数の戻り値を検査してコード を書くことはほとんどなく、 C 言語の仕様ではわざわ ざ v 。 id 型にキャストする必要もありません。この例で はライプラリ関数の printf が対象なので、ちょっとや りすぎという気もしますが、自分で書いた関数の場合に は意味が出てくるでしよう。本当に値カ坏要であれば、 関数の定義を変更して戻り値をもたないものにすれば いいのです。その意味では、この誤りを検出する機能 は便利かもしれません。 関数宣言の不一致 exit 関数を加えたことで新たに出力されたメッセー ジについてみてみましよう。実行例 4 では、 llib-lc の 237 行目と sum. c の 14 行目とで exit の戻り値の 149
% cat 1 2 実彳」 6 18 } —n sum . C extern void 3 main (argc , argv) 4 5 7 8 9 10 11 12 13 14 15 16 17 % lint int char sum . C argc ; int int first exit() ; atoi(* + + argv) ; first, 1ast; i , sum = 0 ; last = at0i (* + + argv) , for (i = first; i く = last; (void)printf("%d\n" , sum) ; exit(O) ; / * NOTREACHED * / sum. c ( 4 ) : warning : argument argc unused in function main も 2 つ目の引数のほうが大きいことなどを検査しなけ ればなりません。これらの検査をおこなえば矼 gc 引数 を使うこともあるでしようが、この例では引数の検査を 省略しているので使われていません。 lint コマンドは、使われていない引数をみつけたとき にはそれを出力します。使われていない引数カワ。ログラ ム中にあっても、とくに間題はないでしよう ( プログラ ムのサイズが大きくなるという欠点はありますが ) 。た だし、プログラムを修正する段階で、引数に正しい値が 入っているものとして修正してしまうと、正しく動かな くなる可能生もあります。 通常の関数の場合は、使われていない引数はたんに プログラムから削除して、その関数を呼び出している箇 所を修正すれば問題ないのですが、 main 関数の引数で はそうもいきません。 argc 引数を削除すると、プログ ラム起重加芋のコマンド行引数を正しく受け取れなくなっ てしまいます。 main 関数でなくても、ほかの関数の引 数として使うようなときは、インターフェイスを合わせ るために必要としない引数を指定することもあります。 これに対処するために、 lint コマンドには一 v オプショ ンがあります。これを指定することにより、関数の引 数のなかに使われていないものがあっても警告を出さ ないようになります。 ただし一 v オプションを指定すると、すべての関数に 152 対して指定か有効になるので注意が必要です。実彳引列 6 の例ではプログラム中の関数が 1 つだけなので問題は ありませんが、複数の関数を含むファイルを lint コマ ンドにかける場合、あらかしめ分かっている引数を使 わない 1 つの関数のために、そのほかの関数に対する 使われていない引数のチェックがおこなわれなくなって しまいます。これでは、機能を十分に使いこなしている とはいえません。 じつは lint コマンドには、ある特定の関数について だけ、使わない引数があるということを示す指定も準備 されています。これも、さきほどと同様にコメントで指 定します。今度は、 ARGSUSED という単語を指定し ます。実行例 6 では戻らない関数の後ろに指定しまし たが、これは使わない引数がある関数定義の直前に指 定します。 % cat sum. c extern void main (argc , argv) / * ARGSUSED * / exit ( ) ; int char **argv ; argc ; int int first 1ast fro (i i , sum = 0 ; first, 1ast ; atoi (* + + argv) ; atoi(* + + argv) ; = first; i く = last; i + + ) { UNIX MAGAZINE 1993.6
(void) printf ( "%d\n" exit(O) ; / * NOTREACHED * / % 1int s ・ um. c sum) ; SUWHP ワークステーション用 拡七リー ローコスト版 X ターミナル ・低価格なから高級機並の機能と 充実したサポート体制 ・柔軟に対応する SIJN. DEC フォント・コンノヾチ ソニー・テクトロニクス製 •SPARC classic ・ LX 8 、 82MB •SPARC station 10 16 、 64MB •SPARC station 2 ・ 1 ・ 1 十ヨ PC 16MB •SPARC station 33 ・ 70 ・ 390 82MB ESPARC se 「 ve 「 60 256M 日 これで、 lint コマンドからの警告やメッセージはす べて消すことができました。 メッセージを消すためだけの本来なら必要のないテ クニックをいくつか使いましたが、これらは lint コマ ンドにプログラムの意味を正しく伝えるのに必要なも のです。プログラムに多くのコメントなどが入っている ことを、恥しることはありません。自分か書いたプログ ラムを読むのは、 lint コマンドだけとはかぎりません。 ほかの人か売むことも考えられます。そのときに、今回 伊題として使ったプログラム sum. c の最初の例 ( 実行 例 1 ) と、修正を加えた最後の例ではどちらか読みやす いと思いますか ? 今回は、、 1 ⅲ t コマンドに文旬を言われないためには どうしたらよいのか " という話になってしまいました。 lint コマンドの出力するメッセージについて、今回紹介 したものはほんの一部にすぎません。実際にどんな出力 がおこなわれるかすべては検査できませんが、手近にあ る 2 つのシステムの lint コマンドを調べたところ、合 計で ( もちろん重複するものは 1 っと数えて ) 200 近 い数のメッセージか準備されているようです。これだけ たくさんのメッセージがあることをみても、 lint コマン ドの賢さが分かるのではないかと思います。 すべての紹介はできませんが、通常の使用でよく見 かけるメッセージとその対処方法、 lint コマンドのオ プション、 lint コマンドをライプラリに対して使用す る方法などについてはおいおい触れていくつもりです。 次号を手にするまでには 1 カ月あります。そのあいだ に、これまで作ってきたプログラムを lint コマンドに かけてみてはいかがでしようか。 ( いまいずみ・たかし東京工業大学 ) UNIX MAGAZINE 1993.6 ■ 3 / 60 ・ 3 / 80 ・ SPA 日 C •SPARC station •SPARC statio ・ 4 / 470 ・ 4 / 490 32 、 1 ■ 3 / 260 ・ 280 ・ 470 ・ 4 / ■ 4 / 110 16MB APOLLO ・ DN3010A ・ 0 ・ ・ DN5500 ■ HP9000 / X 8 、 1 、 32 atiO L C B 4 、 8MB 32 3 / ー HP900 •MA •Ne c S B E HP) 00 4 、 dMB / 750 64MB 16 、 82MB NeXT K ・ QIJADRA 000 4 Sta i n ■ NEWS 721 ・ 1 1500 4MB ■ NEWS 1600 ・ 1700 ・ 3460 4MB •NEWS 3260 ・ 3470 16MB 'SIJN/SPARC station 対応 520MB ~ •MACINTOSH 対応 213MB ~ ESONY/NEWS 対応 24 ロ MB ~ ・ HP9000 / 400 ・ 700 対応 213M B— ◎光磁気旧 mm , DAT バックアップシステム 拡張メモリー ハードティスク l.05GB— DAT バックアップシステム SUNWS についてもお見積りします。 ※記載されている会社名または製品名は、各社の商標または登録商標です。 ※アフターケアには万全を期しております。 ・お気軽にお問い合せ下さい。 信頼と実績の電子機器商社 Est.1 955 宝商式 〒 103 東京都中央区日本橋 3 ヨ 3 ーⅢ油脂工業会館 ) 営業第 2 部担当 / 須賀 TEL(03) 3274 ー 2433 FAX ( 03 ) 327 ト 8023 資料請求 N 。 .00D ハードティスク及び周辺機器 旧 M RS / 6000 対応 153