践 C プログラミング つけにくくなります。とくに複数の人て、開 と書いておけばいいのて、す。 extern をつけ のて、す。上の sub. c の場合は sub. h は List 3a 発する場合は混乱が待っているだけて、す。 れば , 実際に x という変数を作るのて、はな のようになります。そして , sub. c の中の変 このようなときは , プログラムのファイ く , ほかのファイルにある x という変数をこ 数や関数を使うファイルの頭に ル ( ソースファイル ) をふたつ以上に分割し こて、も使うという意味になります。 #include ” sub. h ” ます。こうすれば , 有効範囲を特定のファ static という語を使えば , 関数の有効範囲 と書き込んて、おくのて、す。 イル内にかぎることがて、きます。 も限定することがて、きます。 sub. c て、は foos このヘッダファイル sub. h は , システムて、 ub( ) という関数の定義に static がついていま List 2a と List 2b は , ひとつのプログラム 用意されたヘッダファイル (stdio. h など ) と すのて、 , foosub ( ) はこのファイル内て、しか をふたつのファイルに分割して記述したも 同じディレクトリに入れるものて、はありま のて、す。このように分割した場合のコンパ 使えません。 せん。 sub. c と同じカレントディレクトリに イル法は , あとて、も説明しますが , 4 月号付 f00 ( ) という関数の定義には static がつい 置いておきます。カレントディレクトリの 録の LSI C ー 86 て、の , ひとつの方法は ていませんのて , ほかのファイルて、 f00 ( ) を ファイルをインクルードするときは , 上の A>lcc -c main.c@l 呼び出して使うことがて、きます。 main. c て、 ようにファイル名を " sub. h " のように二重引 A > lcc -c sub.c@) これを使うためには , main. c の先頭に 用符 ( ダブルクオート ) て、囲みます。これに A > lcc main. 0bj sub. obj(é double foo(int x) ・ 対して , システムのヘッダファイルは < std て、す。こうすれば main. exe という実行ファ と書いておきます。ただし , 古い C(UNIX io. h> のように角カッコて、囲みます。 List 3b のように , sub. h は sub. c 自身て、も イルがて、きます。 の cc など ) の場合には ファイル sub. c の 1 , 2 行目て、は整数型の変 double f00 ( ) ; インクルードします。こうしておくと , su とします。古い C て、は引数の型のチェック b. h と sub. c とて、矛盾があればコンパイラが教 数 x, y を作っていますが , y のほうには sta tic という語がついています。この static は「こ が行われません。 えてくれます。 のファイルの中だけて、しか使えない」という しかし , もし sub. c て、 double x ; と定義し ファイル分けの例 : 意味て、す。ほかのファイルからこの y を読み たものを main. c て、間違えて extern int x ; と 乱数の生成 書きしようと思っても , 不可能て、す。変数 したりすると , コンパイルは正常に行われ の定義にはて、きるだけ static をつけておきま るのにて、きあがったプログラムは誤動作す ファイルを分ける実例として , 乱数 ( て、た らめな数 ) を作る関数を収めたファイル rnd. るという困ったことになります。こういう static がつかない単なる int x ; のほうは , 食い違いを避けるため , 通常は次のように c と , これをテストする関数を収めたファイ ほかのファイルからも読み書きて、きます。 ル rndtest. c を作ってみましよう。 します。 たとえば main. c て、この x を読み書きしたいな sub. c を書く際に , sub. c の中のものて、ほか 乱数を生成するライプラリ関数は C 言語に のファイルて、も使ってよいものは , sub. h と 標準て、備わっていますが , ゲーム用ならと ら , main. c の先頭に いうファイルにその宣言を書き込んて、おく もかく , 科学計算には , 中身のわからない extern int X ファイル sub. c List 有効範囲を考える List 2a 1 int x; 1 : static int y; 2 : 3 : static int foosub(void) 7 : double foo(int x) 9 : 10 : } 0 1 よりなっ 0 4 -0 6 ー 8 9 0 14 りっ 0 -4 0 ^ 0 叮ー 8 9 、↓ 1 ↓ 1 ・ 4 1 よ 1 よ 11 1 よ 1 よ 11 、ー 4 int 1 : for (i int X ; i 十十 ) i く 10 : ファイル main. c List 2b 1 : extern lnt x; 2 : double foo(int x); 3 : main() 5 : 実践 c プログラミング入門 85
92 ) をご覧ください List 4b に戻りましよう。 3 行目の static unsigned long seed= 1 , は static unsigned long seed て、変数 seed を作り , その初期値を 1 にすると いう意味て、す。このように , 初期値は変数 を定義した際に代入してしまうのが簡単な 方法て、す。 static をつけない変数て、も , この ような初期化はて、きます。 実は , この static unsigned long seed= 1 ; を関数 irnd ( ) の定義の中に入れてしまうこ ともて、きるのて、す ( List 5 ) 。 rnd ( ) の働きは変わりません。先ほどと同 様 , seed に初期値 1 を与える操作は , コンパ イル時に一度行われるだけて、す。 irnd() を 呼び出すたびに seed に 1 を代入するという意 味て、はありません。ただし , こうすると see d は irnd( ) の外側からは絶対に読み書きて、き なくなるのて、 , 先ほどのように別の関数 ini t rnd ( ) <seed の値を変えるということはて、 きません。 なお , List 5 て、もし seed の定義に static を つけなければ , 呼び出しごとに seed は 1 に初 期化されてしまいます。また , 仮に初期化 「 = 1 」を省いても , 前回の呼び出し時の seed の値は保存されません。 List 4 に戻り , これらのファイルをコンパ イルしてみましよう。まず次のようにひと つずっコンパイルします ( 4 月号付録 LSIC ー 86 の場合 ) 。 rnd. c, rndtest. c のどちらが先て、 もかまいません。 A > lcc -c rnd. c(é A > lcc -c rndtest. cél これて、 rnd. obj , rndtest. obj というオプジェ クトファイルが作られます。最後に A > lcc rndtest. 0切 rnd. objru と打ち込むと , このふたつのオプジェクト ファイルとライプラリがリンクされ , 実行 ファイル rndtest. exe がてきあがります。引 数の順序を逆にして A > lcc rnd. obj rndtest. 0切材 とすると , 実行ファイル名が rnd. exe になり UNIX の cc なら , lcc を cc に , . obj を . 0 に読 み換えてください。て、きた実行ファイル名 は a. out になりますのて、 mv a. out rndtest のようにして好きな実行ファイル名に改名 します。 UNIX て、は実行ファイル名に . exe はつけません。 上て、 lcc(cc) に与えた一 c オプションは「コン パイルだけせよ」という意味て、す。 -c をつけ ないて、 rnd. c を lcc て、処理すると , まずコンパ イルが行われて , rnd. c というソースファイ ルから rnd. obj というオプジェクトファイル が作られます。そのあとて、 , rnd. obj にライ プラリなどがリンクされて , 初めて実行可 能な rnd. exe になるのて、すが , この場合は rn d. c に main() がありませんのて、 , lcc て、は Undefined symbol.• main というエラーになります。 また , main( ) がある rndtest. c て、も , 単に A > lcc rndtest. c(é としただけて、は , 関数 rnd() の定義が rndte st. c の中にはありませんのて、 Undefined symbol: というエラーになります。 されたエラーについては , にがつきます。 make rnd リンク時に検出 関数名の前か後 実際に複数のファイルからなるプログラ ムをコンパイルする際には , 前逑のように はせず , make ( メイク ) というツール ( 道具 ) を使います。 make は UNIX 起源のツールて、す。 MS-D OS2€ソコンはインテルの 80X86 という CPU だけて、すが , UNIX マシンにはいろいろな C PU があるため , フリーソフトの配布はバイ ナリ ( コンパイル済みプログラム ) て、は都合 が悪いのて、 , ソースコードて、配布します。 これを利用者がコンパイルするのて、すが , 何十ものファイルにわかれていることが多 いため , ひとつひとつコンパイルするのは 大変て、す。そこて、 , ソフトの著者は makefi 践 C プログラミング le ( または Makefile) というテキストファイ ルにコンパイルの手順を書いておき , これ をソースに同梱して配布します。 ソフトを組み立てるには make と打ち込む と , make というプログラムがカレントディ レクトリの makefile を読み込んて、 , そこに 書いてある手順どおりにコンパイルしてく れます。 make は , ソースコードの一部を手直しし て再コンパイルする際に真価を発揮します。 再度 make と打ち込むと , make は各ファイ ルのタイムスタンプ ( 更新時刻 ) を比較し , 手直ししたファイルの影響がおよぶものだ け再コンパイルします。 MS-DOS にも make. exe というツールがつ いていますが , これは幼稚なおもちゃなの て、 , 捨ててしまって , コンパイラについて いるものを使いましよう。 LSI C ー 86 にも立 派な make. exe がついています。 先ほどの乱数プログラムを作るための ma kefile を書いてみましよう。もっとも簡単に 書くと List 6 のようになります。 字下げしてある行は , 行の頭て、必ずタブ キーを 1 回押して字下げします。 この makefile の意味は次のとおりて、す。 まず , 1 行目の rndtest. exe : rndtest. Obj rnd. Obj は , 「 rndtest. exe が最終目的て、あるが , これ は rndtest. obj と rnd. obj とから作る」という意 味て、す。て、はどうやって作るか。それが次 の 2 行目 lcc rndtest. Obj rnd. Obj て、す。このとおりに打ち込めば , rndtest. 0 bj と rnd. obj とから rndtest. exe がて、きます。 て、は rndtest. obj は何から作るか。これが 3 行目の rndtest. c rnd. h rndtest. Obj : て、す。これは「 rndtest. obj は rndtest. c と rnd. h とから作る」という意味て , て、はこれらか らどうやって作るかというのが , 次の 4 行目 の lcc -c rndtest. c て、 , このとおりに打ち込めば作れるとし ことてす。 rnd. h は rndtest. c の中の 実践 c プログラミング入門 、フ 87
Dr. 望洋の ープロクラミンク、道場 p 「 in ”耻 ) と putchar n') 標準出力ストリームに対して , 改行を出力 するには , ( 1 ) printf( ” *n ” ) : ② putchar( ' *n') : 、のふたつが , もっともポビュラーな方法だ。 ②のほうが優れている理由を考察しよう。 ・本文ても解説したように , ( 1 ) の記述が複 数個あるしそれぞれに対してく \ n \ 0 > の 2 バイトずつもの領域が取られる可能性 がある。 ・ ( 1 ) の呼び出しは , 引数がポインタてあ る。したがって , ポインタが 4 バイトて、あ る環境てあれば , 4 バイトもの引数がコヒ。 ーされることになる。 80X86 のラージデー タモデルてあれば , printf 関数は , セグメ ントの切換えなどの , ややこしい処理を内 部的に行うことになる。しかし②の引 数は int 型だ。引数として渡されるのはポイ ンタてなく単なる値てあり , セグメント云々 といった負担もない ・②の putchar は , 引数と受け取った文字を そのまま出力するだけてある。一方 , printf は , 書式指定がどうなづているのだろうか と , 文字列を先頭からなぞりながら処理を 行わなければならない。したがって , ( わず かとはいえ ) 実行速度も遅くなるだろう。 printf は万能てはあるものの , puts や putchar の存在を否定するものてはない。 [ 教訓 ] 適材適所 ? ? ? 。 て , int *p = NULL , と同じ意味を持つ。しかし , p のビットパタ ーンがすべて 0 てあるとはかぎらないのだ。 NULL'* インタの内部的な表現がどのよう になっているかは , 処理系に依存する。 【重要】スルポインタの内部表現のビ ットパターンが 0 てあるという保証はない char *p [ 10 ] ; memset (), 0 , sizeof (char * ) * 10 ) ; は , char へのポインタの配列を定義し , そ れをヌルポインタて初期化しようという意 図て、あるが , これが正しいという保証はな いのてある。 ところが , 以下の定義によると , 正しく ヌルポインタて初期化が行われるのてある。 static char * X ; static char * p [ 10 ] ; なぜならば , 静的記憶寿命を持つオプジェ クトに対して , 明示的に初期値を与えない と 0 ( すなわち , この場合はヌルポインタ ) て 初期化されることが保証されているからて ある。 x も , 配列 p の各要素も , すべてヌル ポインタとなるのて、ある。 なお , 関数の外て、宣言・定義するオプジ ェクトは ,static を指定しなくても静的記憶 寿命を持つのて、 , static はなくてもかまわな とめ 下記必要事項を明記のうえ , フロッヒ。ーデ また希望に応じてプログラムの添削も行う。 本連載て、は , 読者からの質問にお答えし , ったことも必要かもしれない 注意が必要だ。指導用語の総チェックとい あれば , ヌルにかぎらず , 用語には細心の もし , 読者カ℃言語を指導されているのて、 りにも無神経というものてある。 ることなく「空」の訳語を与えるのは , あま な」を表している null に対して , 深く考察す えても問題はない。しかし , 明らかに「無効 たキーワードに , すべて「ヌル」の訳語を与 本来の英語が n ⅶなのだから , 今回説明し まとめよう。 しまうこともあり ) その余裕はない。簡単に ( C 言語やプログラミングの本質とは離れて のネ意性について話すべきなのだが , 味を持っことを最初に述べた。本来ならば , 、、 nu にには , 「無効な」 , 「空の」という意 イスクにて応募していただきたい ( 短い質問 てあれば , 書面のみて可 ) 。 なお , 応募いただいたフロッピーディス クは , 返却てきないことをあらかじめご了 承されたい どんな小さな疑問点ても結構なのて , 気 後れせずに , 応募していただきたい ( ただ し , 特殊な題材よりも , 一般性の高い題材 を優先的に取りあげることを , あらかじめ ご了承願いたい ) 。 ( 1 ) 氏名・住所・電話番号 ②匿名希望の有無 / ペンネーム ( 3 ) 質問・相談事項 ( なるべく具体的に ) 宛先 〒 108 東京都港区高輪 2 ー 19 ー 13 NS 高輪ビル ソフトバンク ( 株 ) 出版事業部 C マガジン編集部 「 Dr. 望洋のプログラミング道場』係 [ 参考文献 ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 6 ] [ 5 ] 平林雅英 , fANSI C 言語辞典』 , 技術 評論社 , 1989 P. J. Plauger and Jim Brodie, 福富 寛他訳 , 「 STANDARD CJ , ソフトバ ンク , 1990 柴田望洋 , 「秘伝 C 言語問答ポインタ 編』 , ソフトバンク , 1991 柴田望洋 , 「 CMAGA セミナールー ムく第 5 回 > C に関するいろいろなこと 991 ①」 , 「 C マガジン』 , Vol. 3 , No. 1 , 1 1991 察」 , ℃マガジン』 , V01. 3 , No. 11 , ムく第 15 回 > 文字列を返す関数の考 柴田望洋 , 「 CMAGA セミナールー 1. 3 , No. 5 , 1991 ・その②」 , ℃マガジン』 , Vo 座く第 2 回 > C てプログラミングしよ 柴田望洋 , 「明解 ANSIC 言語入門講 しばたばうよう ( 工学博士・九州大学工学部化学機械工学科 ) したがって , 122 C MAGAZINE 1 2 11
はなく , て、たらめに増えたり減ったりしま の整数型の上限・下限が書き込まれていま ものは使わないほうが安全て、す。ここて、あ す。ただし , この irnd ( ) は 4294967296 回呼 す。 unsigned long 型の上限はほとんどの処 げる rnd. c は一応まともなはずて、す。 irnd ( ) 理系て、 び出すと最初の値に戻ります。 は整数 (unsigned long 型 ) の乱数を , rnd ( ) #define ULONG MAX 4294967295 rnd( ) は , irnd( ) の戻り値を実数 4294967 は 0 以上 1 未満の実数 (double 型 ) の乱数を生 296.0 て、割り算して , 0 以上 1 未満の実数の乱 のように定義されているはずて、す。 16 進法 成します。乱数の初期値を変更するには ini て、書けば 0xFFFFFFFF て、す。これに実数 数に仕立て直します。 t rnd (x) という関数を呼び出して行いま 変数 seed は , init rnd( ) と irnd( ) て、共有 1.0 を加えれば 4294967296.0 が得られるはず す。この引数 x は任意の整数 (unsigned lon する必要がありますが , ほかのところて、不 てすが , 4 月号付録 LSIC ー 86 コンパイラて、も g 型 ) て、す。乱数生成法の詳しい解説は D. E. Knuth 著 , 渋谷政昭訳「準数値算法 / 乱数』 用意に変更されたりすると乱数の系列が狂 SunOS 4.1.2 の C コンパイラて、も , この部分 ってしまいますのて、 , static 宣言するのが望 ( サイエンス社 , 1981 ) や伏見正則「乱数』 を unsigned long のまま計算するのて、分母が 「 0 による除算」工ラーが起きてコ ましいのて、す。 0 になり , ( 東京大学出版会 , 1989 ) を参照ください ンパイルてきません。 要するに irnd ( ) は , 呼び出されるごとに ちなみに , List 4b の #define FAC ( 1.0 / 4294967296. の seed という変数 ( 初期値 1 ) を 1566083941 倍し それから , LSI C ー 86 の limits. h の値は少し 間違っているようて、す。たとえば int 型の下 は ANSI C て、は て 1 を加え , その値を返しているだけて、す。 seed は unsigned long 型て、すのて、 , 0 以上 42 限 INT MIN は ( ー 32767 ー 1 ) のはずて、す #include く limits. h > #define FAC(I O/(ULONG MAX 十 1 . の ) が , ー 32767 になっています。正しい limits. h 94967295 ( 16 進法て 0xFFFFFFFF ) 以下の の書き方については P. J. Plauger 著 fThe とすべきものて、す。 limits. h というへッダフ 値しかとれません。桁あふれした分は捨て ァイルには , そのコンパイラて、扱える種々 Standard C Library 』 (Prentice-HaII, 19 去られますのて、 , 値はどんどん増えるのて、 rnd. h ファイル sub. h List List 3a 1 : extern lnt x; 2 : int foo(int x); 1 : void init—rnd(unsigned long x); 2 : unsigned long irnd(void) ; 3 : double rnd(void); List ファイル sub. c 3b rnd. C List 4b 0 O ・ 1 ・ 1 0 ⅲねね 1 人りな 00 -4 ′ 0 6 叮ー 8 9 0 1 よ 1 よ , 1 1 : #include ” rnd. h ” 2 : #define FAC ( 1.0 / 4294967296. の 3 : static unsigned long seed = 1 ; 4 : void init—rnd(unsigned long x) seed = x; 6 : 8 : unsigned long irnd(void) 10 : seed ニ seed * 1566083941UL + 1 ; return seed; 11 : 12 : } 13 : double rnd(void) / * 0$rnd()<1 * / 14 : { return FAC * irnd() : 15 : 16 : } ファイル main. c List 3c 1 : #include ” sub. h ” 2 : main() 4 : Li st rndtest. c irnd ( ) のもうひとつの実現法 ・ 1 O O ・ 1 ・ 1 1 人り 00 -4 -0 6 叮ー 8 9 0 List 5 1 : unsigned long irnd(void) static unsigned long seed = 1 : 3 : 4 : seed = seed * 1566083941UL + 1 : return seed; 6 : 86 C MAGAZINE 1992 11
ル新 9 ・・しなかったらどうなるだろう」 List 1 に今の話を適用してみましよう。 Lis さて , ここて、何かヒ。ンときませんか ? 「必 「もし・・ t 1 て、は , puts with 10g を使う前に必ず ope と考え「自動的に・・・・されるようにする方法 ず・・・・・・しなければならない」というところに n 10g を呼ばなくてはならない , という状況 はないだろうか」と考えを進めてみるのて、 はエラーがつきものなのて、す。「ご飯を食べ す。いいて、すか。整理するとこういう順序 もし open 10g を が生まれてしまいました。 る前には必す手を洗ってね」「あ , 忘れた」「帰 呼ばなければどうなるて、しよう。変数 10g ー f りがけに必ず葉書を買ってきてね」「あ , 忘 て、すね。 p は NULL が入っているのて、 , fputs に NUL れた」 L が渡され , Null pointer assignment が発 必ず・・・・・しなければならない コンヒ。ュータなら , プログラムに書かれ 生してしまいます。もうひとつの問題は , ている順序を間違えることはありませんが , open 10g は二度呼んて、はならないという点 人間は「必ずしなくてはいけないこと」を間 ・・しなかったらどうなるか て、す。 List 1 を見ていただければわかるよう 違えたり忘れたりするものてす。 に , open 10g を二度呼ぶと , ファイルをも 自動的に・・・・・・て、きないか 困ったことにプログラムを書くのはコン う一度オープンしなおしてしまうのて、 , い ピュータて、はなく人間て、す。プログラムを この思考の流れは「丈夫なプログラム」を ままて、ログファイルに書き込んだデータは 書いていて「必ず・・・・・・しなければならない」 作るための重要なポイントて、す。具体的に 失われてしまいます。 という局面が出てきたら要注意て、す。 そこて、 , 考えを進め「自動的にログファイ ログファイルに書き込む関数 ( 弱い版 ) ルが一度だけオープン」て、きないかを検討し てみましよう。それが List 2 て、す。関数 ope n 10g は削除してしまい , ログファイルのオ ープンを puts with 10g の中に組み込んて、し まいました。変数 10g ー fp が NULL て、あるとい うのはまだファイルがオープンされていな い印なのて , それを使ってオープンをする かしないかの条件判断をしています。それ にともなって大域変数 10g fp を関数内の sta tic 変数にしました。関数内の変数にした理 由は , ほかの関数から 10g ー fp をいじられたく なかったためてす。 static にする理由はわか りますね。それから念のため , fputs の後に こまて、の内容が確実にデ fflush を入れて , イスクに吐き出されるようにしました。 一応この関数はこれて、よしとします。丈 夫なプログラムの感じがおわかりて、しよう か。 List 1 を List 2 に書き換えたときの発想 法は伝わりましたか。 行数える関数 例題 2 ファイル名を引数としてファイルの行数を 数える関数 count ⅱ ne を作りなさい。 さて今度は , 工ラーコードについて考え ↓ List 1 : #de f i ne LOGF I LE ” *}LOGF I LE ” 2 : 3 : FILE *log—fp = NULL; 4 : open—log(void) 5 : VOid if ((log—fp = fopen(LOGFILE, 、” ) ) = = NULL) { 7 : fprintf(stderr, "Xs : cannot open*n", LOGFILE) ; 8 : exit(-l); 9 : 10 : 13 : VOid 14 : { puts—with_log(char *message) puts(message) : fputs (message, log—fp) ; fputs ( ” \ n ” , log—fp) ; ログファイルに書き込む関数 ( 少し強い版 ) LiSt 1 : #define LOGFILE ” **LOGFILE ” 2 : 3 : void puts_with_log(char *message) 5 : static FILE *log_fp = NULL; 6 : puts(message) : 7 : if (log_fp ー = NULL) { 8 : if ((log_fp = fopen(LOGFILE, " 響 " ) ) ー = NULL) { 9 : fprintf(stderr, ” Xs : cannot open*n ” , LOGFILE) ; 10 : 11 : exit(-l); 14 : fputs(message, log—fp) ; fputs( ” *n ” , log—fp) : fflush(log—fp) : プログラミングの工ッセンス 111
List 9b Fig. 1 依存関係の鎖 25 : ( 29 : ) 32 : { 34 : } 48 : } 53 : } 57 : { 6 : = 1 : 2 : #include く stdlib. h> 3 : #include く stdarg. h> 4 : #include く string. h> 5 : #include く limits. h> rndtest. exe rndtest. Obj rnd. Obj rndtest. C rnd. C rnd. h 9 : 11 : 12 : 14 : 17 : 18 : 20 : 23 : 26 : 28 : 30 : 33 : 35 : 36 : 38 : 39 : 41 : 42 : 43 : 44 : 45 : 49 : 52 : 54 : 55 : 56 : 58 : 59 : 62 : 63 : 64 : 65 : 66 : 68 : 69 : 71 : 77 : 80 : 81 : 82 : 83 : 7 : #include く dos. h> 8 : #include ” mylib. h ” 10 : #define CR ' *x0d' static union REGS regs : 13 : static char buf [ 2048 ] ; 15 : int dgetc(void) / * MS-DOS 1 文字入力 * / regs. h. dl ニ 0xff; regs. h. ah = 6 ; do { intdos(®s, ®s) : } while (regs. x. flags & 64 ) : return regs. h. al; 24 : void dputc(char c) / * MS-DOS 1 文字出力 * / / * ゼロフラグ * / List List 4 の makefile その 1 6 lcc -c rnd. c 5 : rnd. obj : rnd. c rnd. h lcc -c rndtest. c rndtest. obj: rndtest. c rnd. h lcc rndtest. Obj rnd. Obj rndtest. exe: rndtest. Obj rnd. Obj 4 : 3 : 2 : 1 : regs. h. dl = c; regs. h. ah = 6 : intdos(®s, ®s) : 31 : void dputs(char (s) / * MS-DOS 文字列出力 * / ist 7 while (*S) dputc()s + + ) ; va 1 ist argptr; int r; int dprintf(char *format, va—start(argptr, format) ; / * MS-DOS printf * / Llst 4 の makefile その 2 1 : # makefi le for rndtest. exe 2 : 0 S ニ rndtest. obj rnd. 0bj 8 : $ ( 0 S ) : rnd. h $(CC) -c $ く . C. Obj: $(CC) $(OBJS) 4 : rndtest. exe: $(OBJS) 3 : CC = lcc 7 : 6 : if ( (r = vsprintf(buf, format, argptr)) 〉 = dputs( ” dprintf() : String T00 Long*n ” ) ; 2048 ) ( abort(); va—end(argptr) : dputs(buf) ; return r; 50 : VOid clearscreen(void) / * 画面クリア * / Li st 8 List 4 の makefile (UNIX 用 ) 1 : OBJS = rndtest. 0 rnd. 0 2 : CC = cc 3 : rndtest: $ ( 0 S ) $(CC) ー 0 rndtest $(OBJS) 4 : $(CC) -c $ く 7 : $ ( 0 S ) : rnd. h ist mylib. h 9a i nt dgetc ()O (d) ; 2 : void dputc(char c); 3 : void dputs(char *S); 1 : 4 : 5 : 6 : 7 : / * MS-DOS 1 文字入力 * / / * MS-DOS 1 文字出力 * / / * MS-DOS 文字列出力 * / . ) : / * MS-DOS printf * / int dprintf(char *format, / * 画面クリア * / VOid clearscreen(void) ; int getlong(char* specialchars, long *value) : / * long の値 value を入力 * / dputs("}x1bC2J ” ) ; int getlong(char specialchars ロ , long *value) int c, len, sign; / * long の値 value を入力 * / if (len = の { while ()c = dgetc()) ! = (R) { len = 0 : *value = 0 : sign = 1 : S1gn S1gn len + + : dputc(c) ; ) else if (c ー *value = C len + + : dputc(c) ; if (isdigit(c)) ( ) else if (strchr(specialchars, c)) { ) else { } else dputc(' %a' ) ; dputc(c) ; break; if (isdigit(c)) { if (*value く = (LONG MAX ー (c ー ' 0 ' ) ) / 1 の { 1 en- } else if (c } else dputc(' *a' ) ; *value = 10 * *value + (c len + + ; dputc(c) ; ist . mylib. c 9b 6 : #include く *. h> 1 : #include く stdio. h> dputc(' *b' ) : dputc(' *value / = 10 : } else dputc('*a' ) ; srgn; 86 : dputc(' *value return 88 C M AGAZINE 1 2 11
List 4 34 : / * ー ときや , アプリケーションが GetMessag た , UpdateWindow 関数が呼び出された ペイントを要求するときに送られる。ま プリケーションのウインドウの一部の再 Windows またはアプリケーションが , ア WM PAIN T WM PAINT の概要を示そう。 ージて、ある。 再描画が必要て、あることを知らせるメッセ 域の一部または全部が無効になっており , て、ある。 WM PAINT は , クライアント領 たに処理するメッセージが , WM PAINT return (DefW i ndowProc (hwnd, mes sage, wParam, メイン関数 1Param)) : e 関数か , PeekMessage 関数を使ってこの メッセージを取得したときにも , Dispat chMessage 関数によって送られる。 Windows によって送られ てくる場合 クライアント領域が無効になる場合は , このようなときに WM PAINT が送られ たとき て裏に隠れた後 , ウインドウを表に出し ・ほかのウインドウが上に重ねて表示され 後 , ウインドウ表示に戻したとき ・アプリケーションを一旦アイコン化した とき ・ユーザがウインドウの大きさを変更した いろいろと考えられるのて、例をあげよう。 てくる。 インドウを表示しただけて、は , クライアノ と呼び出したことを覚えているだろう。ウ UpdateWindow (hwnd) ・ すかさず ( ! ) UpdateWindow 関数を , 最初に Window を作成・表示したときに 生成する場合 アプリケーションか自ら 62 C MAGAZINE 1992 11 ればならないのて、ある。 を生成し , 該当するウインドウに送らなけ とによって , 自ら WM PAINT メッセージ そこて、 , UpdateWindow 関数を呼び出すこ ト領域は無効て、あり , 有効にはならない Fig. 31 : 32 : ) 33 : 35 : 36 : 37 : 38 : 39 : { 40 : 41 : 42 : 43 : 44 : 45 : 46 : 48 : 49 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 60 : 61 : 62 : 63 : 64 : 66 : 67 : 68 : 69 : 70 : 71 : 72 : } 1 1 int PASCAL WinMain(HANDLE hlnstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) static char szAppName[] ” He110Wolrd ”・ RegisterClass(&wndclass) ; wndclass. lpszClassName wndc 1 ass. 1 pszMenuName wndc las s. hbrBackground wndclass. hCursor wndclass.hlcon wndclass. hlnstance wndclass. cbWndExtra wndclass. cbClsExtra wndclass. lpfnWndProc wndclass. style if ( !hPrevInstance) { wndclass; WNDCLASS msg; MSG HWND hwnd; = LoadIcon(NULL, IDI—APPLICATION) ; = hlnstance; WndProc; ニ CS_HREDRAW ー CS_VREDRAW; = GetStock0bject(WHITE_BRUSH) ; ニ LoadCursor(NULL, IDC_ARROW) : szAppName ; = NULL; NULL, NULL, hlnstance, NULL) ; CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, WS_OVERLAPPEDWINDOW, hwnd = CreateWindow(szAppName, ” Hel 10 World ! " return (msg. wParam) : DispatchMessage(&msg) ; TranslateMessage(&msg) ; while (GetMessage(&msg, NULL, 0 , の ) { UpdateWindow(hwnd) ; ShowWindow(hwnd, nCmdShow) ; hello. exe の実行例 オプション ( 0 ) プログラムマネージャ ウインドウ M ヘルプ ( H ) \ 平旦 dQ Ⅵ ' 准裕 Vo 盟 . H 訓 0. WorId ! \'Vs 工 0 Ⅵ
NULL, NULL, ShowWindow(hwnd, nCmdShow) ; UpdateWindow(hwnd) ; whi le (GetMessage(&msg, NULL, 0 , TransIateMessage(&msg) ; DispatchMessage(&msg) ; return (msg. wParam) ; DialogBox TabIe 25 DialogBox の概要 LiSt 61 : 62 : 64 : 65 : / * ー 66 : 68 : 69 : 71 : 72 : 73 : 74 : 75 : 76 : 77 : ニ CS HREDRAW ー CS_VREDRAW; 79 : 80 : 82 : 83 : 84 : 85 : 86 : 88 : 89 : 90 : 92 : 93 : 94 : 95 : 96 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 1 2 return (DefW i ndowproc (hwnd, メイン関数 message, wParam, lParam)) ; 特集 ープログラミンク は , ダイアログボ、ツクスのスタイルを指定 STYLE WS CAPTION ー WS SYSMENU 縦方向は高さの 8 分の 1 を単位とした値て、あ 向はシステムフォントの文字幅の 4 分の 1 , int PASCAL WinMain(HANDLE hlnstance, HANDLE hPrevInstance, LPSTR 1 ps zCmdParam, int nCmdShow) ” He110 Wolrd ”・ static char szAppName ロ する。 こには , ウインドウスタイル (Tab HWND hwnd; MSG msg; WNDCLASS wndclass ; hlnst = hlnstance; if ( !hPrevInstance) { wndclass. style wndclass. lpfnWndProc wndclass. cbClsExtra wndclass. cbWndExtra wndclass. hlnstance wndclass. hlcon wndclass. IpszMenuName wndc 1 as s. hbrBackground wndc las s. hCursor = WndProc ; = hlnstance; ニ LoadIcon( NULL, IDI APPLICATION) ; = LoadCursor(NULL, IDC-ARROW) ; = GetStockObject(WHITE BRUSH) ; ” Hel loMenu ” ; wndclass. lpszClassName : szAppName; RegisterCIass(&wndclass) : hwnd = CreateWindow(szAppName, ” Hel 10 World ! ” WS OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hlnstance, NULL) ; の ) { 形式 解説 int DiaIogBox(HANDLE hlnstance, LPSTR lpTempIateName, HWND hWndParent, FARPROC lpDialogFunc) : ゆ Tem ate に指定されたダイアログボックステンプレートに定義されたサイズ , スタイル , コン トロールを持つモードっきダイアログボックスを作成する。 hWndParent は , ダイアログボック スを所有するアプリケーションのウインドウを示す。ゆ DialogFunc パラメータが指す関数は , ダ イアログボックスが受け取るすべてのメッセージを処理する。 戻り値ダイアログホックスを終了させるために使われた EndDialog 関数の nResu はの値を返す ログボックスは , ェデイタ上て、作成するこ ともて、きるし , 各種のダイアログボックス 工デイタによる作成も可能だ ( 後者の使用法 などについては , マニュアルなどを参照し ていただきたい ) 。 最初の行は , AboutBox DIALOG 22 , 17 , 144 , 72 て、 , ダイアログボックスの名前を AboutBo x と指定している。 DIALOG の後の数値は , ダイアログボックスの左上隅の x 座標 , 同 y 座標 ( 親ウインドウのクライアント領域の左 上隅を ( 0 , 0 ) としたときの座標 ) , 幅 , 高さ て、ある。 なお , この単位はドットてはなく , 横方 le 11 参照 ) を指定する。 指定しなかった場合は , WS POPUP ー WS BORDER ー WS SYSMENU が自動 的に設定される。 CAPTION ” About Hello-WorId' は , タイトルバーに表示するキャプション ー 1 , 0 , 14 , 144 , 8 ー 1 , 0 , 5 , 144 , 8 BEGIN を指定する。 CTEXT "Hello, World!" CTEXT ” Microsoft Windows 特集 MS-Windows プログラミング基礎学 71 だが , ここて、は詳細な解説は省略する。 ロールに対するキー操作などのために必要 最後の WS GROUP は本来 , 複数のコント 場所と大きさを指定する。 続く 4 個のパラメータは , コントロールの OK を返すように指定している。 こて、ブッシュボタンを押した場合は , ID ジを送らないのて、 , ー 1 としている。また , して送る ID て、ある。テキストは , メッセー 2 番目のパラメータは , 親ウインドウに対 ボタンを指定するものだ PUSHBUTTON は ,デフォルトのブッシュ ボ、ツクスの横方向中央に表示される。 DEF 指定するのて、 , 、、 Microsoft Windows" は , CTEXT は , センタリングしたテキストを EFPUSHBUTTON のふたつがある。 ロールを記述する。 こて、は , CTEXT, D テキスト , アイコン , ボタンなどのコント ログボックスの具体的な内容だ。各行には , BEGIN と END て、囲まれた部分が , ダイア END 旧 OK , 53 , 50 , 32 , 14 , WS GROUP DEFPUSHBUTTON ” OK ー 1 , 0 , 30 , 144 , 8 CTEXT ” Version 3.0 '
て、す。このため , あらかじめ経路の向きを 時計回りか反時計回りのどちらかに揃えて おけば , 4 通りのショートカットの組み合わ せのうち 2 通りだけを調べればよいことにな ります。このプログラムて、は , 三角形を作 る段階て、向きを調べ , 時計回りて、あれば回 転方向を逆転して , すべての図形が反時計 回りになるように向きを揃えています。も との三角形が反時計回りに統一されている のて、 , 統合された経路も反時計回りになり ます。三角形の回転方向を調べるのにはべ クトルの外積を利用しています。 イ 分割処理 分割処理は割合単純て、す ( List 4 ) 。ここて、 行っているのは , 図形が縦長か横長かを調 , 縦長て、あれば横分割 , 横長て、あれば縦 分割を行うことて、す。分割といっても実際 には , 縦・横いずれかの方向にソートして いるだけてす。前述のようにソートにはラ イプラリ関数 qsort を用いています。 qsort に は比較関数のポインタを引数て、渡しますが , comp h と comp v のふたつがそれにあたり ます。 comp h は横方向 , comp v は縦方向 のソートを行うときに用います。 マージ処理 分割して求めた経路を統合する部分がマ ージ処理の関数 merge て、す (List 5 ) 。この関 数は , 共有点のサーチ , ショートカットパ スの決定 , ポインタのつなぎ換えという二 つの部分からなります。 まず , マージの基準点て、ある共有点をサ ーチします。これは単純に座標比較て、サー チしているだけて、す。環状リストだと , デ ータの「端」というものがないのて、 , こうい う処理には便利て、す。次に , 2 通りあるショ ートカットパスのうちから , 効果の大きい ほうを選びます。つなぎ換えの効果は関数 effect により算出します。この関数は , 単純 List 6 xyC1] = xyC2]; 153 : xyC2] = 154 : temp ; 155 : p = xalloc(sizeof(*p) * n); 156 : (i ニ 0 ; i く n; i + + ) { 157 : for pCi]. = xyCi]; 158 : pCi]. next = &pCi + 1 ] ; 159 : if (i 〉の 160 : pCi]. prev = &PCi 161 : 162 : pC0]. prev = &p[n ー 1 ] ; 163 : p[n ー 1 ]. next ニ &pC0] ; 164 : 165 : #ifdef DISPLAY draw link(), 7 ) ; 166 : 167 : #endif 168 : return p; 169. 170 : 171 : / * ニ頂点が同一であれば 0 を返す 172 : unit_t comp_xy(point_t *pxyl, point_t *pxy2) 173 : 174 : 175 : un i t_t comp : 176 : comp = PXYI->X ー PXY2->x; if (comp ! = の 177 : 178 : return comp ; 179 : return pxyl ー >y ー pxy2¯>y : 180 : 181 : 182 : / * 二点間の距離を求めて返す 183 : unit_t distance(point—t *pl, point—t *p2) 184 : 185 186 : 10 dx, dy, 響 : 187 : dx = pl- 〉 x ー p2->x; 188 : dy = pl- 〉 y ー p2->y; 響 = sqrt()x * dx + dy * dy); 189 : return (unit t) 町 190 : 191 : } 192 : 193 : / * ショートカットの効果を距離として返す 194 : unit_t effect(point_t *PO, point_t *pa, point—t *pb) 195 : 196. return distance(pa, (O) + distance(pb, ) ー 197 : 198 199 : 200 : / * テストデータのセット 201 : void set_xy(point—t xy ロ , int n) 202 : 203 int i; 204 : for ( i 205 : 206 : 207 : 208 : 209 : } 210 : 211 : / * メインプログラム 212 : int main(int argc, char *argvC]) 213 : 214 : { static point—t XYCMAX XY] ; 215 : 216 : int n_point; 217 : if (argc く 2 ) { 218 : fprintf(stderr, " 使用法 : tsp 頂点数 \ n " ) : 219 : exit(l); 220 : 221 : init—graphics() ; 222 : n—point = at0i(argvC1]); 223 : set_xy(xy, n_point) ; 224 : divide and conquer(xy, n—point) ; 225 : 226 : printf( ” hit any key ・ 227 : (void)getchar() : 228 : 0 0 ′ー 1 、 0 L.O 6 0 十んⅣん 82 C MAGAZINE 1 2 11
ログラマのための List 2 List 2 58 : 59 : 60 : 61 : 64 : 66 : 68 : 69 : 73 : } SDPB; 74 : 75 : typedef struct { / * 内部変数領域 / * DOS ファンクション 52h の ES:BX が指し示す領域の内容 77 : / * mcbtop; 先頭の MCB のセク・メント・アト・レス / * 00h void far *ßdpb; DPB の先頭へのインタ 78 : / * 04h 内部 FCB フ・ロックの先頭へのホ。インタ * / void far *pfcbhead; / * 08h CLOCK テ・ハ・イスを指すホ。インタ void far *ßx)lkdev; / * 0Ch CON テ・ハ・イスを指すホ。インタ 81 : vo id far qmndev : 接続ト・ライフ・中で最大のハ・イト / セクタ * / 82 : uns ignedmax secbyt : / * 10h void far ; / * 12h 内部テ・イスク・ハ・刃アの先頭アト・レス 83 : 内部ト・ライフ・情報の先頭アト・レス void far *Xirvinf; / * 16h 84 : / * 1Ah void far *prsvl; 不明 85 : / * 1Eh 86 : uns igned rsv2 : FCBS=x, y の y 接続ト・ライフ・数 Char drivenum; / * 20h unsigned / * 21h LASTDRIVE ト・ライフ・数 88 : unsigned char lastdrive; LASTDRIVE=Z ならば 1Ah 89 : 90 : } SDOSVAR ; 91 : 92 : void prdrive( SDPB far * ) , search ( char * , char * , int ) , prfile( struct find_t * ) , usage( void ) : 93 : 94 : SDOSVAR far *getdosvar( void ) : 95 : 96 : int main( int argc, char *argv[] ) 97 : { / * 内部変数領域のアト・レス 98 : SDOSVAR far *Xiosvar ; A_HIDDEN ー _A_SYSTEM / * 検索属性 99 : int attr A_SUBDIR; 100 : = getdosvar() ; / * 内部変数領域のアト・レス 101 : harderrl int24h ) ; int 24h ハント・ラの設定 102 : if ( argc ← 1 ) { 103 : prdrive( ) ; / * ト・ライフ・構成の表示 104 : return( 0 ) : 105 : 106 : : 2 & & argv[ 1 ] [ 0 ] : if ( argc ー 107 : usage(); / * 使用法表示 108 : return( 1 ) : 109 : 110 : while ( --argc ) ( 111 : static char rootdir[] ー” ? : 料 112 : char schdirC PATHLEN ] ; / * 検索テ・イレクトリ 113 : / * DPB 114 : SDPB far *ßipb; 115 : for ( + + argv, xipb : ; / * 最初の DPB 116 : FP_OFF( xipb ) ! : 0xffff; / * DPB のチェーンをたどる * / 117 : xipb : -osmajor 〉 = 4 ? 図 p ト > u. dos4. pnextdpb 118 : : EOb->u. dos3. pnextdpb ) { 119 : rootdirC 0 ] ニ ( char ) ( gdpb->drive + ' A' ) : 120 : strcpy( schdir, rmtdir ) : 121 : search( schdir, *argv, attr ) : / * テ・イレクトリ検索 122 : 123 : 124 : return ( 0 ) ; 125 : 126 : 127 : 128 : void prdrive( SDPB far *xipb ) / * ト・ライフ・構成の表示 129 : { ュニットセクタ長クラスタ長 DIR 数クラスタ数テ・イスク容量 \ n ” ) ; printf( 130 : printf( " 131 : do { DPB のチェーンをたどる 132 : printf( ” Xc: Xu u % 6u % 7u % 7u % 101u \ n ” , 133 : xipb->drive + ' A' , IOb->unit, 134 : xlpb->sec-byte, ( xlpb->clsizel + 1 ) * xipb->sec-byte, 135 : 136 : gipb->dirnum, 図 b->data_size ー 1 , ( unsigned 10 ダ ( pdpb->data-size ー 1 ) , 137 : ( EOb->cIsize1 + 1 ) * ) : 138 : xipb : -osmajor 〉ニ 4 ? 図 p ト > u. dos4. pnextdpb : EdPb->U. dos3. pnextdpb; 139 : ) while ( FP-OFF( Edpb ) ! = 0xffff ) : / * 最後の DPB チェック ? 140 : 141 142 : 143 : / * ディレ外リ検索 144 : 145 : * / 00g t00 0h00 ー hf 、 10 凸 int attr ) ョ 148 : 149 : { 150 : struct find t sdta; char PATHLEN ] : 151 : / * 検索テ・イレクトリセット strcpy( schEhth, schdir ) : 154 : strcat( schfile ) : / * 検索ファイルセ外 155 : if ( dos findfirst( &sdta ) - attr, 156 : printf( ” YnXsYn ” , schdir ) ; / * 検索テ・イレ外リ do { 158 : 159 : 160 : 161 : 162 : 163 : 164 : 165 : 166 : 167 : 168 : 169 : 170 : 171 : 172 : 173 : 174 : 175 : 176 : void prfile( struct find_t ) 177 ・ union udate { 178 : struct { 179 : 180 : wr_time; 181 : wr date; ) sdatel; 182 : struct { 183 : 184 : uns s 2 : 5 ; ヨ 185 : 186 : hour : 5 ; 187 : uns igned day : 5 ; 188 : uns month : 4 ; 189 : year : 7 ; } sdate2; 190 : ) udate; 191 : 192 : udate. sdatel. wr-time = rnta->wr-time; / * 最終更新時刻 193 : udate. s 面 tel. ” date = 図ねー > 響 r date ・ / * 最終更新日付 194 : printf( " % -14S 記 % c 記 % 101u [ lx % 6u ー % 02 に % 02u u : % 02u : % 02u \ n " , 195 : 196 : 〉 name, A_SUBDIR ) ? 'd' ( ßdta->attr i b & 197 : 198 : ( 〉 attrib & -A-ARCH ) ? ' 199 : 200 : A_SYSTEM ? ' EHta->attr ib & 201 : A HIDDEN ? 'h' gdta->attrib & -AZRDONLY ) ? ' 202 : r W 203 : Äita->s i ze, / * ファイルサイス・ 204 : xita->s i ze, / * ファイルサイス・ 205 : udate. sdate2. year + 1980 , / * 年 206 : udate. sdate2. month, / * 月 207 : udate. sdate2. day, / * 日 208 : / * 時 udate. sdate2. hour, 209 : udate. sdate2. mrn, / * 分 udate. sdate2. sec2 * 2 ) : 210 : つ 211. 212 : 213 : void usage( void ) / * 使用法表示 214 : static char *msgCJ = { 215 : " \ 033 [ 36m ファイル検索ューティリティ ハ・一シ・ヨン 0.01 \ n " , 216 : "Copyr ight (C) 1991 N. Nakashi \ 033 [m#n#n ” , 217 : ”使用法 1 : search*n}n", 218 : 219 : ト・ライフ・構成を表示します。 }n*n ” , " 使用法 2 : search くファイル 1 > [... くファイ加 > ] \ n \ n " , 220 : 221 : すべての接続ト・ライフ・を検索します。 #n*n ” , ”使用法 3 : search -h#n*n ” , 222 : 223 : ヘルフ。・メッセーシ・を表示します。 *n*n ” , 224 : 225 : 226 : register char **pmsg - msg; 227 : while ( **pmsg ) 228 : printf( *pmsg + 十 ) : 229 : 230. 231 : defined( TURBW_ ) 232 : #if int int24h( void / * int 24h ハント・ラ 233 : 234 : #elif LSI_C ) int far int24h( void ) / * int 24h ハント・ラ 235 : 236 : #e lse 237 : void far int24h( void ) / * int 24h ハント・ラ 238 : #endif 239 : { hardresume( HARDERR_FAIL ) ; / * フ。ロク・ラム上のシステム・コールの失敗 * / 240 : defined( ー R 眦 ) Ⅱ defined( LSI_C ) 241 : #if return ( HARDERR FAI LJ; / * 不要 : コンハ。イル warning 対策 242 : 243 : #endif 244 : 245 : 246 : / * 247 : 返り値内部変数領域のアト・レス 248 : ネ / 249 : SDOSVAR far *getdosvar( void ) 250 : { struct SREGS segs ; 252 : union REGS iregs; 253 : iregs. h. 曲 = 0X52 ; intdosx( &iregs, &iregs, &segs ) : 255 : return( FP( segs. es, iregs. x. bx ) ) : 256 : 257 : } struct dpbstruct far *pnextdpb; / * 次の DPB を指すホ。インタ / * 最後に変更したクラスタ番号 unsigned int last_cl; / * 空きクラスタ数 unsigned int free cl; } dos3; struct { / * セクタ数 / FAT int fat size; int dir—place;/* トト・テ・イレ外リ領域開始論理セクタ番号 far *ßdevhead; / * テ・ハ・イス・ト・ライハ・のホ。インタ void / * メテ・イアテ・イス刎フ。タハ・仆 uns i char ia ; unsigned char diskchange; / * テ・イスク交換を示すフラク・ ・ / * 次の DPB を指すホ。インタ struct dpbstruct far *pnextdpb, / * 最後に変更したクラスタ番号 unsigndi int last cl; / * 空きクラスタ数 unsignai int free cl; } dos4; prfile( &sdta ) : } while ( _dos-findnext( &sdta ) = / * ファイル情報の表示 / * 次ファイル検索 strcpy( schvnth, schdir ) ; / * 検索テ・イレ外リセット strcat( schlhth, ” *. ネ” ) : if ( dos_findfirst( schERth, _A_SUBDIR, &sdta ) dö { if ( sdta. attrib & _A_SUBDIR ) { / * テ・イレクトリ指定チェック if ( sdta. name[ 0 ] = . テ・イレクトリスキ刃。 continue; spr intf ( schlhth, ” % s % s 料” , schdir, sdta. name ) : search ( schgnth, schf i le, attr ) : / * ディレ外リ検索 } while ( -dos-findnext( &sdta ) = / * 次ファイル検索 / * ファ秘情報の表示 / * 最終更新時刻 / * 最終更新日付 / * 秒 / 2 / * 分 0 ~ / * 時 0 ~ / * 日 1 ~ / * 月 1 ~ っ 0 1 人り 0 -0 0 もっ 0 1 人 : 1980 ~ 10 進 ) 16 進 ) / * 検索ディレクトリ / * 検索ファイル / * 検索属性 / * 内部変数領城のアト・レスの取得 96 C MAGAZINE 1992 11