X 68 k 活用講座 LiSt 1 ール開始」のフラグをたてて , 実際 ースクロ にラスタースクロールを行います。 最初は , 画面上方から画面中程まて、がラ スタースクロール範囲になります。スクロ ール範囲の画面上て、の下限がゲーム進行に 応じて下方に移動していきます。これを管 理している変数が end raster て、す。画面全体 がラスタースクロールを行うようになると , end raster は実際には存在しないラスター位 置まて、値が大きくなります。これをそのま ま設定すると「ハマリ」ます。存在しないラ スター位置て、割り込みを設定すると二度と 割り込まなくなってしまいます。最初はこ れにやつばり「ハマリ」ました。 最終ラスター位置の値を , カット & トラ イて、設定するという非科学的な方法て、設定 したのは私て、す。 次に , マップが進行していって , ラスタ ースクロール終了位置が画面に表示される と , ラスタースクロール範囲の上限が画面 下方に向かって移動するようになります。 これを管理しているのが start raster< す。 start raster が最終ラスターを越えて移動し た時点がラスタースクロールの終了てす。 TabIe 1 に List 1 の説明をまとめておきま す。 今回のテーマて、ある半透明機能を忘れる ところてした。半透明機能は , 垂直帰線割 り込みて設定を行っています。 List 2 は gam e. h に追加した変数て、す。 int use half tone 半透明機能を有効にするフラグて、す。実 際には割り込み処理て half def data をポ ートに設定します。 unsigned short half def data 割り込み処理て実際にポートに設定する データをセットしておきます。 List 3 はかなり肥大化した垂直帰線割り込 み処理てす。スプライトの処理 , グラフィ ックパレットの変更 , バックグランドの書 き換え , スクロールレジスタの設定 , 半透 明機能の設定と , ゲームの重要な部分をす べて引き受けるもっとも下位の関数てす。 242 : スクロールレジスタアドレス * / if (do—raster) 243 : 244 : 245 : / * スクロールレジスタアドレスり CRTC REG *crtc = (CRTC_REG * ) 0Xe80018 : 246 : short difs = sin—tbl[index_val][count] : 247 : 248 : next + = RASTER_STEP : if 0 last 報 end-raster く RASTER HAX & & end く next) 249 : ras ter 250 : 251 : next = end_raster; 252 : last 253 : else if (next 〉 RASTER—MAX) 254 : 255 : last : if (last) 256 : 257 : if (start—raster > RASTER_MIN & & start 258 : く RASTER_MAX) raster 259 : next S tart_ras ter : 260 : else 261 : next = RASTER MIN; 262 : last = count = difs = 0 ; 263 : 264 : raster—val. sc2—x—reg = raster—val. sc3 scroll data. sc2_ + difs; _reg —x—reg X 265 : *crtC = raster_val; 266 : 267 : else 268 : next = RASTER_MIN; / * 次回割り込み位置を指定する * / 269 : *next_inter = next; 270 : 271 : count 十十 ; if (count > 32 ) 272 : 273 : count = 0 : IRTE ( ) ; 274 : 275 276 : 277. ・ VOid 278 : 背景移動処理 (void) 279. 280 : static int cont = 0 : 281 : static int cont_block = 0 ; if ( ! 面初期化 ) 282 : 283 : if ( ! 面クリア ) 284 : 285 : 286 : 287 : 288 : 289 : 290 : 291 : 292 : 293 : 294 : 295 : 296 : 297 : 298 : 299 : 300 : 301 : 302 : 303 : 304 : 305 : 306 : 307 : 308 : 309 : 310 : 311 : 312 : 313 : 314 : 315 : 316 : 317 : 318 : 319 : 320 : 321 : 322 : 323 : 324 : 325 : 326 : 327 : 328 : 329 : ・ 1 ・ 1 = PAL_C_MIN + (cont > > 2 ) % (PAL_C_MAX ー PAL_C_MIN) : = 0 ; i く PAL—C-MAX ー PAL_C_MIN; i 十十 ) c-palet[i] = g-palet[j]; if ( + + j > PAL-C-MAX) j = PAL_C_MIN; i f (cont—bl ock く H-BLOCK—NUM) now_counter write—one—bIockO ( ) : write—one—blockl ( ) : if (cont & 1 ) scroll_data. scO—y—reg scro 1 1 data. scl_y_reg scrol l_data. sc2—y—reg scro 1 1 data. sc3—y—reg : scro 1 1 _data : ras ter va 1 sc 「 011_counter 十 : 2 ; if ( + + index-val > 31 ) index val i f ( ras ter-map [scro ll—counter/32] ) end_ras ter + = 2 ; i f (end—ras ter > RASTER—MIN + RASTER STEP) do_ras ter : if (end—raster > RASTER—MIN & & raster—nap[scroll-counter/32] s tart_ras ter + : 2 ; i f (s tart—raster > RASTER—MAX ー RASTER STEP) do raster : 0 : end_raster = start—raster = RASTER MIN; cont 十十 : if ( + + cont—block > 31 ) cont_block = 0 ; if (now_counter : : 面クリア = 1 : return : X68k 活用講座 123
問題 2 ー 7 List 何が表示されるて、 List 3 をご覧ください しようか。 何か表示されるか ? 1 : #include く stdio. h> 2 : 3 : main() int a = 1 , b = 2 ; 5 : 6 : if (a = 7 : printf( ” A*n ” ) ; 8 : 9 : else if (a 10 : printf( ” B*n ” ) ; 11 : 12 : else if (a ! = 2 ) ( 13 : printf( ” C*n ” ) ; 14 : else { 16 : 【問題 2-6 】 printf( ” D}n ” ): 18 : 何が表示されるて、 List 6 をご覧ください 19 : } しようか。 【問題 2-4 】 こて、は , 問題としてプログラムを示し List 4 をご覧ください。何が表示されるて、 ます。このプログラムをコンパイルして実 行したとき , 画面 ( 標準出力 ) には何が表示 しようか。 されるて、しようか。中には引っかけ問題も あるのて、注意してくださいね。 【問題 2-1 】 何が表示さ それて、は List 1 をご覧ください れるて、しようか。 【問題 2-2 】 何が表示されるて、 List 2 をご覧ください しようか。 【問題 2-5 】 List 5 をご覧ください。何が表示されるて、 しようか。 問題 2 ー 8 List 【問題 2-7 】 何が表示されるて、 List 7 をご覧ください しようか。 問題 2 ー 4 1 : #include く stdio. h> 2 : 3 : main() int x = 1 , Y = 2 ; 5 : 6 : 7 : 8 : 9 : 【問題 2-3 】 問題 2 ー 1 List Li st ・ 1 : #include く stdio. h> 2 : 3 : main() 5 : 1 : #include く stdi0. h> 2 : 3 : main() printf("Xd*n", 1 + 2 * 3 ) ; 5 : printf("Xd, Xx*n ” , 255 , 255 ) : 問題 2 ー 9 LiSt 問題 2 ー 5 List 問題 2 ー 2 List 1 : #include く stdio. h 〉 2 : 3 : main() int p = 0 ; 5 : 6 : if (p = の { 7 : printf(" ゼロ *n") ; 8 : 9 : else { printf( ”ゼロでない *n"); 11 : 12 : 13 : } 1 : #include く stdiO. h> 2 : 3 : main() int i ; 5 : 6 : i 十十 ) for (i = 0 : i く 10 : 7 : printf( ” 8 : 9 : 10 : ) 1 : #include く stdio. h> 2 : 3 : main() printf("X5. lf*n", 1.25 ) ; 5 : 問題 2 ー 3 List 問題 2 ー 6 LiSt 問題 2 ー 1 0 List 十・ , 1 よ・、 0 ・ 1 0 0 ・ 1 0 ・ 1 1 より 00 4 -0 6 行ー 8 9 0 1- 00 14 14 1 人 1 : #include く stdi0. h> 2 : 3 : main() int n = 50 ; 5 : 6 : if (n > 5 の { 7 : printf("Rainy*n") ; 8 : 9 : else { printf("Fine*n") : 11 : 12 : 13 : } 1 : #include く stdi0. h> 2 : 3 : main() int i; 5 : 6 : for (i = 0 ; ・ i く 10 : i + + ) ( printf("Xd*n", i * i) 8 : 9 : 80 C MAGAZINE 1993 9
動作は次のようになります。 され , 改行される らかが大きければその値を , 等しければそ ①整数型の変数 x を定義し , 1 て、初期化する 確かに if 文にきた時点て p の値は 0 になって の値を変数 x に代入するのて、す。 ②整数型の変数 y を定義し , 2 て、初期化する いますが , 表示は「ゼロて、ない」になります。 【問題 3-3 】 ③ x 十十 : を実行した結果 , x は 2 になる コンピュータはそこに書かれている日本語 の文章の意味を知ることはて、きません。コ x 十十 : は x を 1 増やすという文て、す。 fo ・意味 r 文の「次の一歩」て、出てきた書き方て、すが , ンピュータは文章て、はなく , プログラムの calc. c というファイルをコンノヾイルしてい みに従うのて、す。 このように for 文以外のところて、も使うこと る最中 , 17 行目て、エラーが発生しました。 がて、きます。これて、変数 x は 2 になり , y は 2 定義されていない age という名前の変数を使 【問題 2-10 】 のままて、す。 っているというエラーて、す。 解答 ④ y = x 十 y : て、 y は 4 になる List 7 から else がふたつなくなった 次のようなエラーが出て , コンパイルに x 十 y ; は , 日本語て、いえば「 x 十 Y y の結果を y に代入する」という文て、す。 失敗します。したがって , このままて、は実 行することはて、きません。 変数 y に代入するのに y 自身の値を使って いるのて、びつくりする方もいらっしやるか Iist10. c 9 : syntax error near ' } ' 解説 もしれません。けれど , この文は正しい C の 文て、す。 ごめんなさい。これは引っかけ問題て、す。 8 行目の printf (... ) の終わりにセミコロン ( ; ) プログラムはまずふたつの変数 x と y の値 がないのがエラーの原因て、す。これを修正 を調べ , その値 ( 両方とも今は 2 て、す ) を加え してコンパイルすれば , ( 結果は 4 て、す ) , その値を変数 y に代入する だけの話て、す。計算の元になった数値が変 数 y から取られたものて、あるかどうかという 点はプログラムは気にかけません。 ⑤ printf て、 x と y の値が表示される 【問題 2-9 】 解答 ゼロて、ないと表示され , 改行されます。 解説 代入演算子 = と比較演算子 = = を混同し ないようにしましよう。動作は次のように なります。 ①整数型の変数 p を定義し , 0 て、初期化する ②条件 p 0 を調べる ( 満たされない ) 【問題 3-1 】 = れは注をが必要て、す。もしも = , ー 「 int 型 ( 整数型 ) の変数 n を定義し , それに かれた条件カ p 0 なら , もちろん満た 整数 50 を代入します。」または , この次のよ されます。 うにお考えくださってもけっこうて、す。「 in こに書かれた条件は p = 0 て、 けれど , t 型の変数 n を定義し , それを 50 て、初期化しま す。これは「変数 p に 0 を代入する」という式 す。」 て、 , 代入の結果 , 式全体の値は 0 になります。 0 は c 言語て、条件を満たさない ( てある ) こ 【問題 3-2 】 とを意味する値なのて、 , この条件は満たさ 変数 a と b の値を比較し , 小さくないほう れません。 ③ else の処理が実行され「ゼロて、ない」と表示 の値を変数 x に代入します。すなわち , どち 84 C MAGAZINE 1993 9 List 1 #include く stdiO. h> 2 int a = 1 を b = 2 : 5 6 if (a = 7 printf( ” A*n ” ) : 8 9 if (a = 1 & & b = 2 ) ー ( 10 printf( ” (心) : 11 12 if (a ! = 2 ) { printf( ” C*n"); 14 15 else ( printf( ” D*n ” ): 17 0 1 4 9 25 36 49 64 と表示されます。 問題 3-4 の解答 List 12 1 : #include く stdiO. h> 2 : 3 : main() int i ; 5 : 6 : for (i = 0 : i く 10 ; i + + ) ( 7 : printf("Xd : % d \ 心を i, 9 ー i) : 8 : 9 : 10 : ) の解答 問題 3 ー 5 の解答 List 13 1 #include く stdiO. 〉 2 int i, j; 5 6 fO で (i ま 0 : i く 10 ; i + + ) ( 7 printf("Xd : " , i); 8 for (j 0 : j く i; j + + ) ( 9 printf("X2d", j + 1 ) : 10 printf( ”” ) : 12 14 )
にはどうしたらよいだろうか ? 確認も List 15 ( 33 頁 ) のように容易に行うこ することて、 , たとえばディレクトリの存在 findfirst( ) て、は , 属性パラメータを指定 st 14 ( 33 頁 ) のようにすればよい Ⅳというファイルの存在を確かめるには Li たとえば , 、℃ : YIMAGEYSAITOH. BM た findfirst( ) を用いる方法て、あろう。 おそらく穏当なのは [ 例題 11 ] て、も述べ 特集実践 C テクニカルファイル List 2 : 13 : { 23 : } ファイルの存在を確かめる List 属性バラメータを指定することでディレクトリの存在確認 / * B0 ⅵ and 糸の処理糸 * / # i nc lude く d i r. h 〉 struct ffblk fen ; if (!findfirst( ” C:WIMAGE%YSAITOH. BMP", 存在する : / * Microsoft 系の処理糸 * / $include く dos. h 〉 struct find_t fen, if (!findfirst( ” C:WIMAGE"SAITOH. BMP", 0xffff, & fen, 0xffff)) { / * Borl and 糸の処理系 * / $include く d ⅳ . h> #include く dos. h> struct ffblk fen, if ( ! fi ndfirst( ” C: %%SOUND"YUUKI", &fen, FA_DIREC)) { 存在する : / * Microsoft 糸の処理系 * / #include く dos. h> struct find-t fen; i f ( ! fi ndfi rst ("C: HSOUND"YUUKI" _A-SUBDIR, &fen)) { 存在する : List 存在する ; ファイルをチェックする List 、、 C : \ L 旧 \ " というティレクトリの存在を確認する 4 : 5 : 6 : 1 : # i nc lude く s td i 0. h> 3 : FILE *fp; 存在する , fclose(fp) ; i f ()p : f 叩 en("C: "IMAGE%%MAKISE. BMP 2 : 4 : 5 : 6 : 1 : $include く stdio. h> 3 : FILE *fp; i f ()p = f 叩 en("C:WLIBHNUL ” , 存在する : fclose(fp); ただ , findfirst( ) は前述したとおり ANS き , 直後に fclose ( ) しておけばよい ファイルポインタを返すのて、存在が確認て、 ファイルが存在すればほば確実に f 叩 en ( ) は ければ fopen ( ) は決して成功しない。そして ープンしてみる方法て、ある。ファイルがな そこて、思いっくのは fopen( ) て、読み込みオ ある。 理系依存になってしまうことが玉にきずて、 I 規格の標準関数て、ないため , その使用が処 とがて、きる。 List 関数を 1 枚被せてファイルの存在確認を行う 2 : 3 : 5 : 6 : 7 : 8 : 9 : 10 : 12 : 20 : 22 : 1 : # i ncl ude く s td i 0. h> int fileexist(char *path) FILE *fp; i f ()p = f 叩 en(path), return 1 : fclose(fp) : return 0 ; int direxist(char *path) FILE *fp; char bufC80] : sprintf(buf, "Xs"NUL", path) : if ()p = f 叩 en(buf, fclose(fp) : return 1 : return 0 : たとえば , 、℃ : YIMAGEYMAKISE. BM の内容を的確に表した関数名を定義 , 使用 数を作成するのてある。このように , 処理 たとえば , List 18 ( 33 頁 ) に示すような関 行うほうがよい った関数を 1 枚被せてファイルの存在確認を コメントをつけるか , fileexist ( ) などとい れた用途て、使用しているため , 必ず正しい f 叩 en ( ) などを , その本来の目的とはかけ離 なお , 蛇足て、あるが , 以上は findfirst( ) , ( 33 頁 ) ) 。 L デバイスて、なくてもかまわない (List 17 とになる。もちろんここて、のデバイスは NU れば当然℃ : \ LIB クの存在確認がて、きた 、℃ : YLIBYNUL" というデバイスが存在す ディレクトリの存在を確認するためには , うことが利用て、きる。℃ : YLIBY" という ィレクトリに存在するはうに見える ) とい るが , MS - DOS て、のデバイスはすべてのデ きないのて、確認することがて、きないのて、あ S て、は fopen ( ) はディレクトリをオープンて、 ディレクトリの存在については , MS-DO List 16 ( 33 頁 ) のようにすればよい P" というファイルをチェックする場合には することは , 特集 ある意味て、プログラム自身が 実践 C テクニカルファイル VoI. 2 33
この場合の , 誤った使用例を Li ていただければ結構て、す。て、すか て、使用しているため , この変数を st 1 に示します。 List 1 の場合は , ら ,List 1 て、は cmdmoji の中身に F グローバル変数として利用してい C コマンド自身 (argv [ 0 ] ) が足り 第 3 パラメータ (cmdmoji) の指定に ると , 知らない間に変数の内容が ません。正しくは , {"fc", 誤りがあります。このパラメータ 書き換えられてしまいます。 ” a. fil ” , は , 通常の C プログラムて、いう ma 'b.fil", NULL} て、なくてはなりま なお , MS-C Ver. 6.0 以降は , ⅲ ( ) 関数の argv に相当すると思っ せんのて、 , List 2 のようになりま この変数名をプログラムて、使用し す。 ても問題は発生しません。 変数名に " を使用したために動作がおかしいプログラム 、、 edata 先頭のパラメータは NULL て、も かまいませんが , 実行する実行フ 0 ァイル . COM , . EXE の名前を記述 MS-C/C 十十 7 . 0 の cout で するのが一般的てす。 浮動小数点数の表示桁数を設定す C 十十プログラムでの spawnl 系 る方法を教えてください。 関数の不具合 List 3 のプログラム ( 拡張子が . C c 。 ut て、浮動小数点数を表示さ PP の場合のみ ) をラージメモリモデ せると , デフォルトて、は , 全体て、 ルて、コンパイル & リンクし , 実行 6 桁の精度しか表示しません。 すると正しく動作しません。 表示桁数を変更するには ios : これは , M S ー C/C 十十 7.0 の precision(int (p) メンノヾ関数を使 C 十十プログラム内の spawnlp 関 用します (List 6 ) 。引数の np には , 数の不具合て、す。 浮動小数点数を表示するために使 この例のように spawnl 系の関 われる有効桁数 , または , 小数点 数は実行する実行ファイル . CO 以下の桁数を表す整数を与えます。 M , . EXE が不定数のパラメータを 表示の書式が指数表現 , または 持っ ( 省略可能なパラメータがある ) 固定小数点表現の場合は , 小数点 場合に不具合が発生します。 以下の桁数を示します。書式が自 回避方法としては , spawnlp 関 動 ( 指数表現て、も固定小数点表現 数の引数にダミーの NULL をつけ て、もない ) の場合は , 全体の有効桁 足してください。 List 3 の場合は , 数を示します。 List 4 のように修正することて、回避 て、きます。 0 MS-C/C 十十 7 . 0 の cout で 表示する際の書式の変更方法を教 0 えてください。 QuickC Ver. 2 . 0 で List 5 の ようなプログラムを作成したので すが正しく動作しません ( Fig. 2 ) 。 c 。 ut て、表示する書式を変更す 原因と対策を教えてください。 : flags(long lFlag るには , ios : s ) メンバ関数を使用します。引数の A List 5 が正常に動作しない原 lFlags には , ビットごとの OR い ) 因は , グローバル変数名にヾ edata 演算子を使って , Table 2 に示すビ という名前を使用しているためて、 ットマスクを組み合わせて指定し す。この変数名は , QuickC 2.0 に ます。 付属の標準ランタイムライプラリ 011t0rsmatj011s な Compi g Makers Fig. 2 List 5 の実行結果 0 272 0 List 5 1 : # i ncl ude く s td i 0. h > 2 : 3 : int edata[3] : 4 : 5 : VOid main(void) 7 : int 8 : for (i=0; i く 3 ; i + + ) 9 : edata[i]=i; for (i=0; i く 3 : i + + ) printf ("%d%n". edata[i]); 13 : 14 : } cout の表示桁数を変更する 1 : #include く iostream. h> 2 : void main(void) 4 : double d = 1. 234567890123 ; 5 : i nt p ; 6 : = cout. precision ( 12 ) : 7 : P 8 : cout くく d; cout. precision (p); 9 : 10 : } List 6 / / 表示桁数を 12 桁に設定 , p に設定前の桁数を保存 〃設定前の桁数に戻す。 ( 必要なければ省略可 ) Table 2 ios . . 利 ags ( ) メンバ関数のピットマスク ヒットマスク 意味 入力された空白文字をスキップする ・ skipws IOS . 値を左詰めにする コ e 負 IOS . 右側は , フィルキャラクタで埋める 値を右詰めにする ・ right 左側は , フィルキャラクタで埋める ( テフォルト ) 符号または基数表示と値の間にフィルキャラクタを追加する . internal IOS . 書式の数値は 10 を基数 ( 10 進数 ) とする ( テフォルト ) . dec IOS . 書式の数値は 8 を基数 ( 8 進数 ) とする . OCt IOS . 書式の数値は 16 を基数 ( 16 進数 ) とする . hex IOS . C 十十コンノヾイラが読み込むことのできる書式で , 数値定数を IOS . . showba 表示する owpoi 浮動小数点数の場合に , 小数点と後続するゼロを表示する 1 6 進数の A から F および指数表現の E を大文字で表示する 正の値のとき , 符号 ( 十 ) を表示する ・ showpo IOS . 浮動小数点数を , 指数表記で表示する . SC ie ntifi IOS . 浮動小数点数を , 固定小数点表記で表示する . fixed IOS . 出力ことにストリームをフラッシュするために ost 「 eam . . unitbuf IOS . osfx を呼び出す ( テフォルトでは , ce 「「がこのモード ) 出力ことに stdout と stderr をフラッシュするために . stdio : osfx を呼び出す ostream . IOS . IOS . lnformation from CompiIer Makers 143
に一彡売 List ファイル名をフルノヾスで標準出力に出力するプログラム 1 : , 指定ディレクトリ以下を再帰的にたどりながら全ファイル名を標準出力に出力 朝 ーヨ 26 : 0 ()f (and (not (str-tail= " \ ” dir)) (not (str-tail= 3 2 : 4 : 6 : 9 : 20 : 22 : 23 : 24 : 28 : 29 : 30 : 32 : 33 : 35 : 36 : 使用例 bonz -f d i r ⅱ s t a: \ b : \ 〉 f00 (defun put-dirlist (dir) , ディレクトリ名の最後が \ でなくかっ : でなければ ()f (and (not (str-tail= ” \ ” dir)) (not (str-tail= (setq dir (str + dir ” \ ” ) ) , 最後に \ を追加 (putln dir) (let (dirlist) : ディレクトリ名を出力 : d i r ⅱ st は d i r 以下のディレクトリを保存するリスト : シンポル f ⅱ e にディレクトリ d i r に存在するファイルを次々に束縛 ー 22 : ー 23 : 朝 27 : 28 : 4 30 : 、 33 : 第 34 : ヨ 35 : ー 36 : 38 : ー 39 : 凵 0 : ) ョ 42 : ーい 44 : 45 : 46 : 47 : ー 48 : ョ 49 : 1 50 : ー 53 : 54 : ー 55 : : 56 : ョ 58 : ヨ 59 : 60 : 4 65 : 66 : 3 68 : 3 69 : ) ! 70 : 73 : 76 : ー 79 : ョ 80 : 1 82 : ー 83 : ヨ 84 : ー 85 : ヨ 86 : ー 88 : 2 : ディレクトリの作成 (let* ((ls (dos-split-fname file)) (dst-file (str + dir (nth 1 ) (nth 2 ) ) ) ) (put file ' - > ” dst-file) ; 日付けを比較 ()f ( > (dos-file-time file) (dos-file-time dst-file)) (system "c 叩 y" file dst-file) (putln コピーしません " ) Li st (foreach file (dos-vexpand 16 (str + dir " *. * " ) ) : f ile がディレクトリでかっ ' " でなければ ()f (and (dos-exist-dir file) (not (str-tail= (setq dirlist (cons file dirlist)) ()f (not (str-tail= ” f ⅱ e) ) (putln file) : ファイル名を出力 (defun make-dir (dir) ” f ile) ) ) (system "mkdir" dir) ()f (not (dos-exist-dir dir)) (exit 1) , d i r 以下に存在するディレクトリについて自分自身を呼び出す (foreach nextdir dirlist (put-dirlist nextdir) , *param-list* はコマンド行引数リスト。例 ( ” thisfile. bon" : *s tderr* はエラー出力ストリーム。標準出力がパイプ / リダイレクトになって (foreach di r (cdr *param-list*) (putln *stderr* " 使用方法 : " (car *param-list*) ' '< 表示開始テ・イレクトリ >. ()f ( く (length *param-list*) 2 ) , いても、文字列は画面に表示される。 コピー元ディレクトリを src 、コピー先ディレクトリを ds t として 凵 3 : : すべてのファイルをディレクトリ構造を保ったままコピー (defun cbpy-dir (src dst) 朝 第 ()f (not (dos-exist-dir dst)) (make-dir dst) (setq src (reg-dir src)) : src に存在するすべてのファイルを dst にコピー (copy-new (str 十 src " *. * " ) dst) (setq dst (reg-dir dst)) , src に存在するすべてのサプディレクトリについて自分自身 (copy-dir) , を呼び出す。 (foreach dir (dos-wexpand 16 (str + src " *. * " ) ) ()f (and (not (str-head= dir)) (dos-exist-dir dir)) (let ((slist (dos-split-fname dir))) (copy-dir dir (str + dst (nth 1 slist) (nth 2 slist))) List ()f (dos-exist-dir dir) (put-dirlist dir) (putln *stderr* dir " はディレクトリではありません " ) MS-DOS の XCOPY コマンドのようなプログラム 63 : ()f ( / : (length *param-list*) 3 ) (let ( ) (putln *stderr* " 使用法 : " (car *param-list*) " く北。 - 元テ・イレクトリ ) > く北。 - 先テ・イレクトリ〉 " ) (exit l) 1 : 3 : 4 : 15 : 9 : 7 : , ファイルをディレクトリごと別のディレクトリにコピー ( ただし隠しファイルやシステムファイルは除く ) , 同名のファイルが存在した場合は日付が新しい場合のみ実際にコピー ( 例 ) bonz -f copydir b:\usr a:}usr , dir の最後が " \ ”でなくかっ” : ”でなければ、 dir に” \ " を追加したものを返す d i r (str + dir " \ ” ) (defun reg-dir (dir) (let ((src (nth 1 *param-list*)) (dst (nth 2 *param-list*))) 1 4 : : ワイルドカードを含む可能性のあるファイル名 fname をディレクトリ d i r に , コピーする。 1 16 : : ただし同名のファイルが存在した場合、日付けが新しいときのみコピーする。 (defun copy-nev (fname dir) (setq dir (reg-dir dir)) (foreach file (dos-vexpand 0 fname) : dos-split-fname はパス、例えば” a:\bin%bonz. exe" を ". exe" ) のように三つに分解したリストを返す ” bonz ” ()f (not (dos-exist-dir src)) (let ( ) (putln *stderr* dir " はディレクトリではありません " ) (make-dir dst) ()x i t の (putln *stderr* "y") ()f (str= (dos-getch) "y") " ディレクトリ " dst " は存在しません。作成しますか ? [ y / n ] (put *stderr* (let ( ) ()f (not (dos-exist-dir dst)) (exit 1 ) (c 叩 y-di r src dst) といったところて、すが , 実際に BONZ のプ す 0List 1 をもう少し実用的に応用したのが て、標準出力に出力するというプログラムて、 的にたどりながら , ファイル名をフルバス よう 0List 1 は指定ディレクトリ以下を再帰 ログラムを見ていただいたほうがよいて、し XCOPY a : *usr b : *usr /s/e List 2 て、す。これは MS-DOS の XCOPY コマ ンドて、 , としたときのように ファイルをディレク トリ ( 以下のサプディレクトリも含む ) ごと 別のディレクトリにコピするというもの て、すが , 同じ名前のファイルがすて、に存在 を加え , ソースのバックアップなどに使用 ファイルのみがコヒ。ー対象になるように手 はこれを , ある拡張子 ( * . c, * . h など ) の ーするという点が異なります。実際に筆者 していた場合 , 日付が新しいときのみコビ しています。 数にファイル属性 , 第二引数にワイルドカ くという関数て、す。 dos-wexpand は第一引 縛しながら第三引数以降を順次評価してい 数 ) に , 第二引数のリストの要素を次々に束 foreach は第一引数のシンポル ( ローカル変 関数 dos ー wexpand の組み合わせて、しよう。 のループ関数 foreach とワイルドカード展開 さて , , こて、特徴的なのは , BONZ 独自 Conference Room 135
特集実践 C テクニカルファイル ANSI 規格に忠実な処理系ならコンパイルエ ラーとなる [ 3 ] ならば , qs 。 rt ( ) へ比較関数のポインタを 渡すところて、 , その「関数へのポインタ」を キャストしてみてはどうだろうか。キャス 目の、 * a, * b" を比較する箇所て、コンパイ トする型は , 最初に説明した、、なんともわか りにくい〃あの型て、ある。 ルエラーとなる。 今度はうまくいった (List 5 ( 27 頁 ) ) 。しか 6 : int int cmp(const void * a, const void * b) し , このキャストはて、きれば避けたい。複 雑なキャストは , 後から見直すときに理解 の妨げとなるし , 処理系の型チェックを台 return 1 ; else if ( * a なしにしてしまう。 10 : てある。なんともわかりにくい型②だが , ANSI 時代の C プログラミングて、は , どう return 0 ; 要するに , Fig. 1 ( 28 頁 ) のような関数へのポ a, b の指す先は int 型て、あることを知って してもキャストが必要な場面はほとんどな インタを渡せばよいのだ。この関数は、、 a , b" い。キャストすることはもはや「下品なこと」 いるのはプログラマだけだ の指す配列要素を比較して , その大小関係 なのだ。 a,b は const void* と宣言されているの て、は , キャストを使わない方法を示そう により以下のような値を返さなくてはなら て、 , コンパイラは、、 a , b ′′の指す先の型がわ (List 6 ( 27 頁 ) ) 。 からず , コンパイルを止めてしまったのだ ・・正の整数値 引数は、、 constvoid* 〃型とし , 比較関数の * a > * b のとき・ て、は , 、、 a , b" を、、 constint*" と宣言した 中て、望みの型へのポインタに代入する。 * b のとき・ らどうだろうか。 ・負の整数値 れならば qsort ( ) へ渡すときにキャストは必 * a く * b のとき int int cmp(const int* a,const int* b) この方法て、先のプログラムを書き こまて、の話は , ライプラリのリファレ 要ない 直してみよう (List 7 ( 26 頁 ) ) 。 ンスマニュアルに書いてある。問題はこの 先だ。整数配列を qsort ( ) て、ソートするプロ [ 例題 7 ] setbuf でファイル グラム (List 4 ( 26 頁 ) ) を作ってみよう。 そうすると , 確かに 8 行目と 10 行目の比較 アクセスを高速化する 一見問題なさそうだが , このプログラム のところのコンパイルエラーは消えるもの は , 比較関数 int cmp ( ) の中の 8 行目と 10 行 ファイルの入出力を高速化するテクニ の , 今度は 34 行目の qs 。 rt ( ) の呼び出して、 , ツ 一番単純な copy ( ) setvbuf()C 高速化した copy() List 「関数へのポインタ」をキャストする qsort(test, ELEMENTS-0F(test)i sizeof(test[0]), ( int ( * ) (const void*, const void*)) int-cmp) : List キャストを使わない方法 int int-cmp(const void* va, const void* vb) const int* a const int* b vb; List List 1 : # i ncl ude く s td i 0. h> 2 : # i ncl ude く s td ⅱ b. h> 3 : 4 : void copy(const char* from, const char* tO, si ze-t bufsize) 6 : i n t C : 7 : F ILE* f i n FILE* fout 8 : 9 : i f ( ! f i n Ⅱ goto err; / * パッファ設定 ( ! ! 追加部分 ! ! ) * / setvbuf(fin, NULL, IOFBF, bufs ize) : setvbuf(fout, NUL し IOFBF, bufsi (e) : 15 : / * 1 パイト単位の転送ループ * / 17 : vhile ()c = fgetc(fin)) い EOF) fputc(), fout) : 20 : ・・・以下省略・ 22 : 23 : } 1 : # i ncl ude く S td i 0. h> 2 : $include く stdlib.h> 3 : 4 : VOid copy(const char* from, to) const char* 6 : i nt c ; 7 : 日 LE* f i n 8 : FILE* fout 9 : 10 : i f ( ! f i n Ⅱ goto err; / * 1 バイト単位の転送ループ * / while ()c : fgetc(fin)) ! ニ EOF) fputc(), fout) ; fflush(fout); / * 工ラーチェックとエラー処理 * / if (ferror(fin) Ⅱ ferror(fout)) { 20 : perror( ” c 叩 y ” ): / * 工ラー原因を表示する * / 22 : done: 23 : fopen (from, fopen(to, !fout) = f 叩 en(from, = fopen(to, !fout) fclose(fout); fclose(fin); 特集実践 C テクニカルファイル VOI. 2 27
マニュアルの訂正 メッセージも出力しないようにし 演算子の説明 TabIe 1 ています。 (x) 式をまとめる働きをします。その値は x です。また , 「 Z80 ではオペラ ンド全体を囲んでいるときには間接アドレッシングを示します 先月の LSI C ー 80 Ver. 3.30 のユ 逆に , List 2 のような void 関数の ( 「 Z80 のみ ) 間接アドレッシングを示します ーザーズマニュアル ( 第 2 版 1992 年 場合は return ( 0 ) ; て , もちろん工 (x) SHR 8 と同じです HIGH x 6 月 ) の正誤表の続きをお知らせし ラーになります。 List 2 て、は , voi (x) AND OFFh と同じです LOW x d ( 関数の戻り値がない ) としている ます。 x をこの式の値とします マニュアル 130 ~ 131 べージの「演 が , 値を return しています。したが x の 2 の補数を値とします 算子の説明」の部分て、 nr80 / rz80 の って明らかにエラーになります (F x と y の積を値とします x * y 演算子に関する説明が追加されま x / Y x を y で割った商を値とします ところて、 , この void がサポート す。 TabIe 1 のようになります。 x MOD y x を y で割った余りを値とします x % y される以前は , List 3 のように関数 x SHR y x を y ピット右へシフトした結果を値とします の戻り値を記述しないて、関数を定 x > > y 義していました。 List 3 のように関 x SHL y x を y ピット左へシフトした結果を値とします x くく y 数の戻り値の指定を省略すると int 0 x と y の和を値とします x 十 y になります。つまり , int は書いて LSI C ー 86 Ver. 3.30 で List 1 x から y を引いた差を値とします のように , 関数の戻り値を int と指 も書かなくても同じ解釈となるた x と y が等しいとき OFFFFh , それ以外のとき 0 め , 実際に戻り値があるのかない 定していながら , 値を return して いないソースをコンパイルしても のかわかりません。 x く > y 、 x と y が等しくないとき OFFFFh, それ以外のとき 0 このような void がサポートされ 工ラーになりません。 x NE y る以前の古いソースをコンパイル このような場合は , せめて警告 する場合 , 警告とはいってもメッ メッセージが出されてもいいので x が y より小さいか等しいとき OFFFFh , それ以外のとき 0 x くニ y x LE y セージがたくさん出るとうんざり はないか ? x が y より大きいか等しいとき OFFFFh , それ以外のとき 0 x > ニ y してしまいます。そこて、現在はメ x GE y A 現在は , id がサポートされ ッセージを出力しないようにして x が y より小さいとき OFFFFh , それ以外のとき 0 x く y る以前のソースを考慮して , 警告 います。 x が y より大きいとき OFFFFh , それ以外のとき 0 x > y int 型関数で「 etu 「 n 値を返さない x GT y x の否定 ( 1 の補数 ) を値とします NOT x int test(); 2 : 3 : int func() x AND y x と y のピットごとの論理積を値とします if (test()) 5 : ーになるべき ? x と y のビットごとの論理和を値とします / * 工ラ x OR y 6 : return : 7 : else return ( の : / * OK * / 8 : x XOR y x と y のビットごとの排他的論理和を値とします x の値が 0 のとき OFFFFh , それ以外のとき 0 になります x も Y も , ともに 0 でないとき OFFFFh , そうでなければ 0 になります x & & y たたし , x の値が 0 ならば y を計算しません x のどちらかが 0 でないとき OFFFFh , そうでなければ 0 になります x Y 是差し , x の値が 0 でなければ y を計算しません 関数型を宣言しない場合 ( int 型 ) lnformation 行 om 物町ⅱ計 Ma れ門 工ル・エス・アイジャノヾン LSI C-80 LSI C -86 Q@A List 1 void 型関数で「 eturn 値を返す func() if (test()) return, e ー se re t u rn ( の ; / * 明らかにエラー * / List 2 1 ーワ朝 00 -4 ・ L.n っー List 3 1 : func() if (test()) 3 : / * 工ラーかどうかわからない * / 4 : return; 5 : e ー se return ( の ; / * 工ラーかどうかわからない * / 6 : Fig. 1 void 関数から値を「 etu 「 n した場合のエラー can't retu 「 n value from 'VOid' function 146 C MAGAZINE 1993 9
ているて、しよう。同様の性質を Creature ク ラスとしてまとめてしまいましよう。火星 人も地球人も同じ太陽系に産まれた生命体 て、す。似通ったところもあろうというもの て、す。 画面上に矩形領域を占めるものなのて、 Re ct クラスから導出します。当たり判定も矩形 の演算て、やろうという論見もありますし ね。自分を描く , 消す , 指定座標に移動 , 相対移動 , やられたときの処理 , ミサイル 関係の打っ , 書く , 消すといったメンバ関 数が並びます。上記を List 1 , Fig. 1 として まとめておきます。 さて , List 1 から自分と火星人のクラスを さらに導出します (List 2 ) 。自分と火星人の 違うところは , 自分は常に画面上にいるが , 火星人はどこからか発生してくるという違 いがあります。そのため , 火星人には存在 を示すフラグと , 発生関数 , やられたとき に抹殺するために b 。 mb ( ) 関数を仮想関数て、 オーバライドします。 実のところ , 今回はあまり仮想関数の恩 恵にあずかる場面はありません。リストな どて、間接アクセスがあれば仮想関数は力を 発揮しますが , 自分と火星人のふたっしか いないのて、直接アクセスしてしまっていま す。 main ( ) は , 画面を設定→オプジェクト生 成→メインループという構成になっていま す (List 3 ) 。メインループは , 自分の処理 , 敵の処理 , 当たり判定をひたすら繰り返し shipProc ( ) , enemyProc ( ) はメンバ関数 に入れるかどうか迷いますが , shipProc ( ) はキーポード判定が必要なこと , enemyPr oc ( ) はもしかして将来たくさん火星人を増 やしたいときに便利かもしれない , と思っ て別にしてみました。 自機の処理は , まずミサイルを処理しま す。ミサイルはとりあえず動かすだけて、す。 その後キーポード処理に入ります。キーポ ードは getch ( ) て、とって , ひとっとったらキ ーバッフアを全部読み出してしまいます。 72 C MAGAZINE 1993 9 亘キーて終了します。 火星人側はミサイルを動かしてから , 火 星人がいれば動かし適当に爆弾を落としま す。火星人がいなければ出現させます ( List 火星人は適当な速さと方向を持って出現 します。上から降りてきてふらふらと得体 のしれないものを落としながら下まて、落ち てきます。なかには降りてこない弱気な火 星人もいます。出現時に横方向の速度 spee dX を設定します。これは右向きが正 , 左向 4 ) 。 Pattern *Pat; char patBaseName [ 13 ] ; public: class Creature : publ ic Rect { 旧 自分と火星人の処理 きが負の数として設定されます。落ちる速 さ speedY は 0 からの値を取るのて、 , 上て、止ま っているヤツが発生するわけて、す。これが いやならば下駄を履かせて , speedY = ra ndom ( 4 ) 十 1 : などとします (List 5 ) 。 火星人きたる ! きます。船の移動はとくに問題ないて、しょ す。火星人は火星人らしく無軌道に降りて 自機はキー入力に同期して左右に動きま int speedX, speedY; / / POint verocity; virtual void move(int aDX, int aDY) VOid moveTo(int ax, int ay); void erase() ; void draw() ; virtual ¯Creature() : Creature() ; public: Pattern *bombPat[5] ; int missileSpeed; POint misOrigin; Pattern *missi lePat ; b001 missile; でもいいかも Fig. virtual void bomb() ; void shot() : void drawMissile(); void eraseMissile(); virtual void doMissile(); 1 自分と火星人の処理 Rect クラス 00 Creatue 「クラス Enemy クラス ( 火星人 ) 0 Ship クラス ( 自分 )
石川 atj 圓 ! 面新 m 川 ~ Ma れ門 FEP を制御するクラス $define Uses-TInputLine #include く tv. h> $include "fepctrl. h ” class TInput い neKanji: public TInputLine { public: TInputLineKanji (const TRect& bounds int aMaxLen) : TInputLine(bounds, aMaxLen) { ) void setState(ushort aState, B001ean enable) : List 3 List 4 リターンで項目を移動するダイアログボックス fep-force-off() ; fep-force-on() : if (enable) i f (aState = sfSelected) { TInputLine: :setState(aState, enable); void TInputLineKanj i : :setState(ushort aState, B001ean enable) TDiaIog(bounds, aTitle), TWindovInit(&TDiaIog::initFrame) { } TNevDialog(const TRect& bounds. const char *aTitle) public: 1 : class TNevDiaIog: public TDiaIog { 2 : 3 : 4 : 5 : 7 : 9 : 13 : void handleEvent(TEvent& event) : void TNevDiaIog: :handIeEvent(TEvent& event) i f (event. vhat evKeyDovn & & event. keyDovn. keyCode selectNext(FaIse); clearEvent(event) : } else { TDialog: :handIeEvent(event); : kbEnter) { Fig. 5 VROOMM を使ったプログラムのランタイムエラーメッセ Runtime overlay e 「「 0 「 COM ファイルへの変換を可能にするためのマクロ 1 : # i fdef NV 2 : $define-ÉARINttf) ((void interr 叩 t far ( * ) ())HK-FP(-CS. f)) List 5 5 : $end i f 4 : #define FARINT(f) f 3 : # se setvect(), FARlNT(handler)) : Fig. 6 List 5 のマクロの使い方 ージ また , クライアントウインドウ やフレームウインドウに貼りつけ たウインドウの大きさを変えると きは ,WM SIZE メッセージに応答 する WMSize 関数を定義して , M oveWindow や DeferWindowPos などを使ってください。 TMDIFr ame の派生クラスからクライアント ウインドウのウインドウハンドル を得るためには , Fig. 3 のようにし ます。 IMPL 旧で , USER. EXE や GD I.EXE など複数のファイルをまとめ てインポートライプラリを作成し ようとしたところ , Fig. 4 のような 工ラーが発生しました。どうすれ ば , このエラーを避けられるでし ようか。 A IMPLIB を実行する前に , IM PDEF を使って対象となるファイル の . DEF ファイルを作成してくださ い。次に , . DEF ファイルを編集し GP や WEP などが定義されてい る行を削除してください。最後に IMPLIB を使ってこれらの . DEF フ ァイルからインポートライプラリ を作成してください Q Turbo Vision で , ダイアログ ポックスに貼りつける 1 行入力 ( T 旧 put 凵 (e) でかな漢字変換 FEP を制 御したいのですが , どうすればよ いでしようか。 0 g の代わりに使ってください。この 新しいクラスを派生して , TDialo A TDialog から List 4 のような が , どうすればよいでしようか。 くリターンキーを使いたいのです ロール間の移動にタブキーではな が , ダイアログボックスでコント Turbo Vision を使っています Q に有効になります。 ルて、はかな漢字変換 FEP が自動的 使うことて , その入力コントロー ne の代わりに TInputLineKanji を を呼び出してください 0TInputLi は , fep init( ) : と fep_term( ) : する際には , main の最初と最後て 実際にかな漢字変換 FEP を制御 生クラスの例を示します。 を派生させてください 0List 3 に派 に , TInputLine から新しいクラス レクトリに展開してください。次 PCTRL. ZIP の内容を適当なディ まず , マスターディスクから FE す。 プラリモジュールが含まれていま イスクには , FEPCTRL というライ A Borland c 十十のマスターデ ダイアログボックスては , ボタン の位置てリターンキーを押しても 次の項目への移動になります。ポ タンを押すためには , スペースキ ーを使ってください Q ォーバレイ (VROOMM) を使 っているプログラムが , 実行中に Fig. 5 のようなエラーを発生しまし た。何がいけないのでしようか。 0 lnformation from CompiIer Makers 155 0 ます。 このマクロは , Fig. 6 のように使い 0 クロを使って回避してください 障害となります。 List 5 のようなマ これが , . COM ファイルへの変換の にセグメント情報が使われます。 ドラのセグメントを取得するため pt ハンドラのアドレスを取るとハン A Borland c 十十ては , interru よいでしようか。 きなくなるようです。どうすれば ると , COM ファイルへの変換がで 使って割り込みハンドラを指定す タイニィモデルで , setvect を Q ださい 問題がないかどうかを確認してく たり部分的にテストするなどて , 生します。プログラムを縮小化し イを継続てきなくなりエラーが発 ャが破壊されると正常にオーバレ ズされたり , オーバレイマネージ み込むファイルハンドルがクロー があり , オーバレイファイルを読 しません。プログラム自体に誤り ているだけてはこのエラーは発生 A 通常は , オーバレイを実行し