define - みる会図書館


検索対象: 月刊 C MAGAZINE 1993年5月号
21件見つかりました。

1. 月刊 C MAGAZINE 1993年5月号

68k 活用庠 : : ゝ、 GCC で学ぶ X68 ゲーム プログラミング 第 18 回 ゲームの仕上げ ( その ] ) 吉野智興 今回から最後の仕上げとして , 今までの連載で扱ってきたす べての X68000 の機能を使ったゲームの作成をします。回を追 うに従っていろいろな機能を加えて , いちおうゲームとして 完成した形にまで仕上げてみましよう。 ついに X68000 にも正統 32 ビットと呼べる X68030 が登場しました。このマシンは XVI の 2 倍以上の処理能力を持っていますが , とスプライトを使ったシューティングゲー ムについては目に見えて高速になるわけて、 はありません。いまだにこの点て、は誤解が 非常に多いのて、すが , 今まて、の X68000 て、普 通に動いていたシューティングゲームて、は 一部の例外 ( ある意味て、はプログラムミス ) を除いて , 機械が 2 倍以上の処理速度になっ ても 2 倍の速度て、動くわけて、はないのて、す。 まともなスプライトを使ったゲームて、は , すべての処理は画面表示をつかさどる CRT 制御部分て、タイミングをはかって処理を行 います。この点は , この連載て、も何度もと りあげてきました。たとえば画面をスクロ ールさせる場合には必ずディスプレイに表 示が行われていないタイミング , つまり垂 直帰線期間にスクロールを行わないと綺麗 なスクロールは行えないことはこの連載の 最初の頃に説明をしました。 て、は , この処理速度の向上はゲームにお 単位時間の戦い ー > spr ー〉 sp_x) ー > spr ー〉 sp_y) List 2 : 7 : 8 : 12 : 14 : 20 : 22 : 31 : 41 : 42 : 45 : #define SPD_WORKO(SP) 44 : #def ine SPD_AN IME_PTR ( SP) 43 : #define SPD_REGIST(SP) #define SPD_LIFE(SP) #define SPD_MOVE_FUNC(SP) 40 : #define SPD_MOVE(SP) 39 : #define SPD_TYPE(SP) 38 : #define SPD_BODY(SP, PART_X, PART_Y) 36 : #define SP_V_INV(SP) 35 : #define SP_H_INV(SP) 34 : #define SP_PCG_DATA(SP, POS) 33 : #define SP_PCG_NO(SP, POS) 32 : #define SP_ANIME(SP) #def ine SP_PCG_DATA_PTR ( SP) 30 : #define SP—DEFINED(SP) 29 : #define SP_PRIORITY(SP) 28 : #define SP_PALET(SP) 27 : #define SP_CODE(SP) 26 : #define SP_POS_Y(SP) 25 : #define SP_POS_X(SP) 24 : #def ine SP_SP_PTR (SP) 23 : #define SP_CHAR_NO(SP) 21 : #define LOAD_DIR ” .. %*DATA}* ” 19 : #endif 18 : #define NULL 0 17 : #ifndef NULL 15 : #define DEBUG 13 : #endif #define EXT 11 : #else 10 : #define EXT extern 9 : #ifndef MAIN #include く interupt. h> 6 : #include く iocslib. h> 5 : #include く i0. h > 4 : #include く stdlib. h> 3 : #include く stdio. h> 1 : / * Cmagazine SampIe ゲームヘッダ * / game. h ((SP) ((SP) ((SP) ((SP) (((SP) (((SP) (((SP) -no) ー〉 spr ー〉 sp_ctrl). sp. sp_code) ー > spr ー〉 sp_ctrl). sp. C010r ) ー > spr ー > sp_pwr). sp. pwr) ー > spr) ー > char ((SP) ー > pcg—defined) (&((SP) ー > pcg-data[0])) ((SP) ((SP) (((SP) (((SP) ((SP) (((SP) (((SP) ( ( SP) ((SP) ((SP) ー > 響 ork の ((SP) ー > anime—def) ー > registance) ー > life) ー〉 def_move). move—func) ー〉 def_move). move_array) - 〉 type) ((SP) ー > body[(PART-X)][(PART-Y)]) - 〉 spr ー > sp_ctrl). sp. v_invert) ー > spr ー > sp_ctrl). sp. h_invert) ー〉 pcg—data[POS]) ((SP) ー〉 pcg_no[POS]) ー〉 anime) 128 C MAGAZINE 1993 5

2. 月刊 C MAGAZINE 1993年5月号

X 68 k 活用講座 List 1 いてどのように現れるかというと , 処理速 度が画面の表示速度に追いつかなくなった 状態のときに顕著に現れます。たとえばコ ナミの「パロデイウスだ ! 」の 2 面て、フル装 ミスて、進むとピエロが大量のリン グを吐き出してきますが , このときクロッ ク 10MHz の X68000 て、は「あれ ? 遅くなっ たぞ」といった現象を起こしますが , XVI の 16MHz クロックて、はそういった現象は起き ません。 X68030 て、は XVI の 2 倍以上の処理速 度なのて、 , 同一時間内に XVI の 2 倍程度の処 理がこなせます。 逆にいえば , 表示キャラクタが少ないと きに X68030 のような高速な機械て、は , CPU はただ垂直帰線期間をウェイトして過ごし ているだけになります。パソコン通信て、 X6 8030 が登場した際にある方が「 2 倍以上速い ってことは , パロデイウスを動かして , つ いて、にグラ 2 が動かせるってことて、これだけ て、もけっこうなことて、は ? 」といっておられ ましたが , まったくそのとおりだと思いま す。残念ながら実現て、きそうにはありませ んけど・・ 0 要するにシューティングゲームは , 完全 にディスプレイのタイミングに束縛された 「単位時間内にどれだけ多くの仕事をこなす か ? 」というあまり C 言語には向かないシビ アなプログラミングの世界ってことになり ます。とはいえ , この連載て、扱う程度のゲ ームは十分に C 言語て、処理て、きるのて、 , 基本 的な事項を理解するといった点て、は問題は ないて、しよう。 ソースを起こす 今回のソースは , スプライト機能の解説 に使った簡単なゲームを基礎にして大幅に 書き換えたものて、す。元のソースはスプラ イトを扱う部分とゲーム本体部分のふたっ だけて、したが , 今回のは「それなりに本格的」 を目指してソースを分割することから始め ます。この記事の時点て、は TabIe 1 のような ((SP) ー〉響 orkl ) ((SP) ー〉響 ork2 ) ((SP) ー > work3) SP_POS_X(SPD_BODY((SP), 0 , の ) SP_POS_Y(SPD_BODY((SP), 0 , の ) ((MOVCCL]). dif-x) ((MOVCCL]). dif-y) 46 : #define SPD—WORKI(SP) 47 : #def ine SPD_WORK2 ( SP) 48 : #define SPD—WORK3(SP) 49 : #define SPD—POS—X(SP) 50 : #define SPD—POS—Y(SP) 51 : 52 : #define MOVE—X(MOV, CL) 53 : #define MOVE—Y(MOV, CL) 54 : 55 : #define NO—USE 0 56 : #define DISP 1 2 57 : #define USE 58 : 60 : #define 表示 1 61 : #define 非表示 0 62 : #define MAX_NUM 118 63 : 64 : / * 機種、コンパイラに完全に依存する記述 * / 65 : / * PCG レジスタの構造体 * / 66 : typedef union { uns igned short dummy ; struct { 68 : unsigned POSO: 4 : 69 : unsigned 8S1 : 4 ; 70 : unsigned 8S2 : 4 ; unsigned POS3: 4 : ) sp; 74 : } SP_REG_U; 76 : typedef SP—REG—U PCGC64] : 77 : 78 : / * スプライトスクロールレジスタ構造体 * / 79 : typedef struct { short int S p—x : 80 : short int sp—y; 82 : union { short int dummy; 83 : struct { 84 : uns i gned V invert unsigned h—invert 86 : uns i gned dummy uns igned CO 1 88 : unsigned sp—COde : 8 ; 89 : } sp; 90 : } sp—ctrl; 91 : union { 92 : short int dummy; 93 : struct { 94 : unsigned dummy : 13 : unsigned ext 96 : uns igned pwr } sp; 98 : } sp_pwr; 99 : 100 : } SP_CTRL; 101 : 102 : / * スプライトデータ構造体 * / 103 : 104 : 105 : typedef struct { SP_CTRL *spr; 106 : Char no ; short 107 : short pcg—defined; 108 : anime; 109 : short pcg—noC8] ; 110 : *pcg—dataC8] ; 111 : PCG 112 : } SPRITE; 113 : 114 : typedef SPRITE *sprite; 115 : 116 : enum SP—tYPe { DOT16 , 117 : 118 : DOT32 ー 1 ー 2 , DOT32 ー 2 ー 1 , 119 : 120 : DOT32 ー 2 ー 2 , DOT48 ー 1 ー 3 , 121 : 122 : DOT48 ー 2 ー 3 , 123 : DOT48 ー 3 ー 3 , 124 : DOT48 3 ー 1 , 125 : DOT48 ー 3 ー 2 , 126 : ) : / * スプライトスクロールレジスタ * / / * キャラクタナンバ * / / * pcg が既に定義されている場合は 1 * / / * アニメーションすれば anime > 0 * / / * アニメ用 pcg 定義番号 * / / * アニメ 4 パターン , g data へのポインタ * / X68k 活用講座 129

3. 月刊 C MAGAZINE 1993年5月号

学 五ロ れています。今回の例て、は , 'DLOG' 1000 のダイアログは 1000 番の ' DITL' を使用しま す。 GetNewDialog は , この情報を使ってダ イアログアイテムリストも一度に作成して くれます。 て、は , 実際にダイアログを処理するコー ドを見ることにしましよう。ダイアログの 処理には ModaIDialog を使います。 Modal Dialog は Fig. 6 のようにプロトタイプ宣言さ れていて , 現在アクテイプなウインドウに 対してダイアログの処理を実行します。し たがって , ModalDiaIog を呼び出す前には ダイアログのウインドウが最前面にくるよ うにしておかなければなりません。 List 1 て、 ShowWindow を呼び出した直後が ModalD ial 。 g を呼び出すもっとも適当な場所て、す。 ModalDialog の第 1 パラメータは , 通常 N ULL にしておきます。第 2 パラメータはマウ スクリックやキーの入力て、選択されたアイ テム番号を格納するための変数のアドレス を渡します。 ModaIDiaIog からリターンし てくると , switch 文を使ってどのアイテム が選択されたのかによって処理を実行しま す。 ModalDialog はアイテムが選択される たびにアイテム番号を返してきます。よっ て , OK ボタンまたは Cancel ボ、タンが選択さ れるまて、ループしなければなりません。 のようすを List 2 に示します。 ModalDialog からはアイテム番号が得ら れます。て、は , そのアイテムの情報にアク セスするにはどうすればよいのて、しようか ? それには Fig. 7 の関数を使います。 GetD Item,SetDItem はダイアログアイテムその もののダイアログ内の領域や , そのアイテ ムがどのような種類のコントロールなのか といった情報を取得 , 設定します。一方 , GetCtIVaIue, SetCtIVaIue はコントロール に設定されている値を取得 , 設定します。 ラジオボタンやチェックポックスて、は , 0 は 選択されていない状態を , 1 は選択されてい る状態を表します。 アイテムの情報にアクセスする具体的な 方法は , まず GetDItem を使ってアイテムの コントロールハンドルを取得し , 次に GetC tIValue あるいは SetCtIVaIue を使ってデー C 言語雑学講座 121 Fig. 7 アイテムのテータアクセス関数 void GetDItem(DiaIogPtr theDialog, short theltem, HandIe *iHandIe, Rect *iRect) ; short *i Type, void SetDItem(DiaIogPtr theDialog, short theltem, HandIe iHandIe, Rect *iRect); short iType, ダイアログを示すポインタ theDiaIog アイテム番号 theltem アイテムのタイプ ( ラジオボタン、ボタン e tc. ) iType アイテムに関する各種情報 ( ラジオボタンやチェックポックスは iHandle コントロールハンドル ) アイテムのウインドウ内の領域 i Rect short GetCtlValue(Contr01HandIe theContr01) : void SetCtlValue(ControIHandIe theControl, short theValue) : コントロールを示すハンドル theControl セットする値 theVa lue コントロールの値 リターン Sample2. c 1 : #include く Types. h> 2 : #include く 0SUtils. h> 3 : #include く Menus. れ〉 4 : #include く DiaIogs. h> 5 : #include く ControIs. い 6 : #include く Desk. h> 7 : #include く T001UtiIs. h> 8 : #include く Fonts. h> 9 : #include く 0SEvents. h> 11 : #define MenuBarID 128 13 : #define AppIeMenuID 128 #define AboutItemID 1 14 : 15 : #define AboutAIertID 128 #define FiIeMenuID 129 17 : #define ShowDiaIogItemID 1 18 : 19 : #define QuitItemID 3 20 : 21 : # de f i ne D i a log I D 1000 #define Radi0ButtonlID 3 22 : 23 : #define Radi0Button21D 4 24 : #define Radi0Button31D 5 25 : #define CheckBoxlID 6 26 : #define CheckBox21D 7 27 : # def i ne Ed i tTex t I D 9 28 : #define UserItemID 10 29 : 30 : #define WNE-TRAP 0X60 31 : #define UNIMPL_TRAP 0x9F 32 : 33 : 34 : B001ean Done; 35 : MenuHandle AppIeMenuH; 36 : EventRecord TheEvent; 37 : 38 : short theltem) 39 : pascal void dravUserItem(DiaIogPtr theDialog, short iType; 42 : Handle iHandle; 43 : Rect iRect; &iRect); GetDItem(theDiaIog, theltem, &iType, &iHandle, PenSize(), 3 ) : 46 : InsetRect(&iRect, 16 ) ; FrameRoundRect(&iRect, 16 , 48 : 49 : } 50 : short theltem) 52 : void setRadioButton(DiaIogPtr theDialog, short iType; LlSt 3

4. 月刊 C MAGAZINE 1993年5月号

List 8 物理行はいったん単語に分割され , 単語 単位て、行に詰められていく。このときに , 必 要に応じて , 英単語のラップや日本語の禁 則が行われる。そして 1 行が完成すると , それ がページに追加され , 1 べージが完成すると ファイルに出力されるという流れて、ある。 ただし , フィルが禁止されている場合には , 物理行をそのままページに追加する必要が ある。その場合には , 物理行を単語に分解 するフィルタオプジェクトが直接物理行を べージへと追加するように渡すことにする。 以上の処理は , 若干手続き指向の香りが いわゆる「ハイプリッド」的なオプ 残った , ジェクト指向のようにも思えるが , Fig. 4 な どと比べると , 主役が fill とか fold などの手 続きから , 単語 (word) とか行 (line) とかの オプジェクトへと逆転していることは明白 て、ある。そして , コマンド処理の細かい内 容などは各オプジェクトが抱え込むために 直接表面には現れてこなくなる。全体とし て構成がすっきりして , 見通しがよくなる。 このような手順からもわかるように , オ プジェクト指向設計というのは , 別にプロ グラム言語を選ぶものて、はない。 C て、も , F ORTRAN て、も , BASIC て、も , オプジェク ト指向設計というものは可能て、ある。 さて , FPLF のもう少し詳しい実例を示す ことがて、きるといいのだが , 架空のソフト ウェアて、もあるため , それは無理て、ある。 そこて、 , もう一度例題 2 に登場してもらおう。 例題 2 を「オプジェクト指向的」に実現する とどうなるのかを考えてみるのて、ある。例 題 2 は非常に簡単なため , オプジェクト指向 らしさを出すには苦しいところがある。た とえば , 継承の概念を利用する余地がない これて、はオプジェクト指向設計のメリット の一部分て、ある , データ抽象としての特徴 しか使っていないことになる。しかし , し よせんオプジェクト指向言語て、はない C を使 ってインプリメントするのて , それはかえ って好都合かもしれない とにかく , オプジェクトとしてふたつを 実装することにした。構文チェッカオプジ 10 : / * 1 : 1 : 3 : 0 62 C M AGAZIN E 1993 5 53 : } 52 : printf("%s = %dYn ” return 0 : *argv, check-double(*argv) ) : 54 : #endif * 構文チェッカ (checker. h) 8 : #define ERR ー 2 7 : #define OK #include ” stream. h" #include く stdlib. h> #define CHECKER_H #ifndef CHECKER_H 1 : 2 : 3 : 4 : 5 : 6 : 9 : 11 : 12 : 13 : 17 : 19 : 20 : 21 : 22 : * 構文チェッカのオプジェクト typedef struct Checker { const int **matrix; i n t s t : int (*stateMachine)(struct Checker *. Stream_t * ) : } Checker-t; Checker_t *checker_nev(const int **matrix) : #define checker-delete(c) free((c)) 23 : #endif * 構文チェッカ (checker. c) 2 : 3 : 4 : 6 : 8 : 9 : 12 : 17 : 1 9 : 20 : 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : #include く stdio. h> $include く assert. h> #i nclude ” checker. h ” * 状態推移解釈ルーチン static int stateMachine(Checker-t *chk, Stream_t *strm) vhile (chk->st ! = OK & & chk->st ! = ERR) chk->st : chk->matrix[chk- 〉 st] [strm- 〉 next(strm)]; return (chk- 〉 st * 文法チェッカを作り出す関数 Checker_t * checker_nev(const int **matrix) Checker-t *chk; chk = malloc(sizeof(Checker_t)) : assert(chk ! = NULL); chk->matrix : matrix; chk->st chk->stateMachine = stateMachine; return chk; * 入力ストリーム (stream. h) 27 : Stream-t *stream_nev(const char (s) : 25 : } Stream_t; charKind_t (*next)(struct Stream * ) : const Char *s; typedef struct Stream { * 入力ストリームオプジェクト } charKind_t; 0ther, Exponent, Point, D i g i t, Sign, White, Eos ニ 0 を typedef enum charKind { * 文字の種別を表す列挙 #include く stdlib. h> #def i ne STREAM_H #i fndef STREAM_H 26 : 23 : 22 : 20 : 1 7 : 9 : 8 : 7 : 5 : 4 : 2 :

5. 月刊 C MAGAZINE 1993年5月号

ロバート・佐々木氏は , 私のご近所の方 で , すでに年齢 70 を越え , 現在は現役引退 の身である。氏は , 2 年前の夏まではパソコ ンをワープロとして使う経験はあったもの の , プログラムのなんたるかはまったく知 らなかった人物である。そのロバート氏が , ' 92 年初頭に C 言語のマスターを決意し , 以 後 , 私 ( 丹羽 ) とミステリアスな C 文通を始め こっちは , 風邪をひいてしまった。もう 前略。元気か ? 丹羽君よ。 っそくだが , 見てみることにしよう。 氏から郵便局経由で手紙が届いている。 さて , 今月も , 同じ町内に住むロバート た。その文通は今でも続いている。 さ 読者氏に対して申し分ない。いや , 申し 小生のプログラムの掲載はない。まった 0 無沙汰してしまった。したがって , 今月は ううむ , おかげで , C 言語とは 10 日ほどご の元であろう。 風邪にはかからんと安心していたのが油断 や , 麦飯を食い , 玄米茶を飲んでいるので , さんが思いをこめて炊いてくれる毒飯 , い 日 , 体に効き目があるといってウチのばあ おそらくは油断したのであろう。まあ , 毎 すっかり春になったと喜んでいたのだが ; 訳ない。 つもりだという。いや , 稼ぎまくるつもり 将来は C のプログラマになって嫁ぎまくる ところで , 中学 3 年になった孫の吉夫が , 誤字ばかりで申し分ない。いや , 申し訳な を変えたのが悪いのかもしれん。まことに どうも , 今日は誤字が多いが , ワープロ たのむと思うがよろしく。 羽君にもお世辞をたのむ , いや , お世話を だという。そのときは , 吉夫のことで , 丹 というのでみてやってほしい。 さて , その吉夫がまたもゲームを作った 円羽信夫 1 . 3 3 : } 吉夫君のゲー プログラミング ム ( 2 ) ( その 17 ) イラスト / 山形彰吾 List #define RIGHT 2 #define LEFT 1 #define STOP 0 $define OFF 0 $de f i ne ON 1 4 : # i nc lude く t i me. h> #include く conio. h> # i ncl ude く s td ⅱ b. h 〉 # i ncl ude く s td i 0. h 〉 game. C 三 第 第 第 第 129 : 1 31 : 13 2 : 1 3 3 : 134 : 1 3 5 : 136 : 137 : 13 8 : 139 : 140 : 1 41 : 142 : 1 4 4 : 1 4 5 : 1 4 6 : 147 : 148 : 149 : 150 : 2 . 3. 5 : 6 : 7 : 8 . 9 : 10 . 1 2 : 1 3 : 1 4 : 15 : 16 : 17 : 19 : 20 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : 30 : 32 : 34 : 36 : 38 : 39 : 40 : 42 : 43 : 45 : 46 : 48 : int screenC40]C23]; / * 画面配列 * / int tekix[10],tekiyC10]; / * 敵の X 座標 , Y 座標 * / i n t はニ 20 : / * なんだったか , 忘れちゃいました . * / i n t c 引 i [ 10 ] ; / * タマとあたっているかどうかの配列 * / int tamax, tamay; / * タマの座標 * / i n t t ama=0F F ; / * 現在タマを発射しているか * / char *tekiC10]= 21 : / * 敵キャラ * / char *kotira= ”土” Char *misile= ” :全” Char *mask= : / * ミサイル移動にともなう消しゴム * / 2 9 : / * カーソル位置を決める * / VO i d loca te ( i n t x, i n t y) printf("YxlbC%d;%dH",y,x); 35 : / * 4 と 6 とスペースキーを押している時 , それらが押されたしるしを返す * / void keyin(int *dir , int *fire) int colison() int i,gameover=(); char ch : ch=0; i f (kbh i t ( ) ) { ニ getch(); swi tch (ch) { case ' 4 ' : *d i r case ' 6 ' : *d i r case =LEFT; break; =RIGHT; break, =STOP; f or ( i ニ 0 ; i <10 : i + + ) if ((tamax==tekixCi)) & & (tamay=tekiy[i]) & & (!coli[i])) co ⅱ [ i ] =ON : tama=0FF; putkotira(lx); tamax=lx, tamay=22; Iocate(I,24); if (gameover!=l の { gameover 十ニC0ⅱ : for ( i = 0 : i <10 ; i + + ) printf( ” X ” ) ; locate(tekixCiJ*2-2,tekiy[i]); 152 C MAGAZINE 1993 5

6. 月刊 C MAGAZINE 1993年5月号

HeX プログラム List 作る いきなりソース 細部の記述を行う前に , プログラムの骨 格を作るのが常套手段て、す。 Windows70 ロ グラミングの場合 , プログラムの骨格とは ューザインタフェイスとも考えられます。 ー隹がいったのか忘れてしまいましたが , ひとつの警句があります。 まずユーザインタフェイスを作れ。さ もないと , データ処理部分の設計を 1 か らやりなおさなければならなくなるで あろう けだし名言て、はありませんか。 ということて、 , 本来ならば段階的に詳し くしていきたいところて、す。てすが , 何ぶ ん誌面はかぎられていますから , 完成した リストのみを示し , その要素要素を少しす つ解説していくことにしましよう (List 1 ) 。 1 : # i ncl ude く s td i 0. h> 2 : # i ncl ude く 0 ⅵ . h > 3 : # i ncl ude く d i r. h 〉 4 : # i ncl ude く s t r i ng. h > 5 : # i ncl ude く j s tr i ng. h > 6 : # i ncl ude く a Ⅱ oc. h> 7 : # i ncl ude く i 0. h > 8 : #include く 0 ⅵ rc. h 〉 9 : # i ncl ude く f ⅱ ed i a g. h 〉 10 : #pragma hdrstop 11 : #include く bwcc. h> 12 : #include ” res. h ” 13 : ” HeX ” 14 : #define PROGNAME 16 : inline char* strtail(char* p) return p 十 strlen(p); 20 : 21 : class WaitCursor { 22 : HCURSOR cursor; 23 : p ub ⅱ c : WaitCursor() 24 : 25 : 26 : cursor Wai tCursor() 28 . 29 : SetCursor(cursor) : 30 : 32 : } : 33 : 34 : #define WIDTH 74 35 : #define HEIGHT 10 36 : : pu b ⅱ c 物 i ndov { 37 : cl ass D i sp 38 : protected: char huge* buffer; 39 : 40 : long len; long yrange; 42 : int cxchar, 43 : int cychar; 4 4 : pub ⅱ c : Disp(PTWindows0bject parent, LPSTR name, PTModule module) 45 : TWindow(parent, name, module) 47 : Attr. Style ニ WS_CHILD ー WS_VISIBLE ー WS_BORDER ー WS_VSCROLL; 48 : (char huge*)farmalloc(l) : buffer ー / / farrealloc() に備えて 49 : 50 : len Scroller = nev TScroller(this, 0 , 0 , 0 , の : / / 仮に 52 : ScrolIer- 〉 AutoOrg = FALSE; 53 : 54 : protected: void GetTextInfo() 55 : 56 : HDC dc = GetDC(HWindow) : SeIectObject(dc, OEM-FIXED-FONT) : 58 : 59 : TEXTMETRIC tm, GetTextMetrics(dc, &tm) ; 60 : ReIeaseDC(HWindov, (c) : t 田 . tmAveCharWidth; 62 : cxchar tm. tmHeight 十 t 田 . tmExternalLeading + 2 ; 63 : cychar 64 : 65 : pub ⅱ c : int neededWidth() 66 : 67 : return cxchar * WIDTH + GetSystemMetrics(SM_CXVSCROLL) ; 68 : 69 : int neededHeight() 70 : return cychar * HEIGHT; int readlt(char* fname) 75 : WaitCursor wait; FILE* fp ー fopen(fname, if ()p = NULL) / / ファイルがない return FALSE; setvbuf(fp, NUL し _IOFBF, BUFSIZ * 16 ) : 80 : filelength(fileno(fp)); len (char huge*) farrealloc(buffer, len + 1 ) : buffer ー 82 : ニ NULL) { if (buffer 83 : : SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC-WAIT))) : インドウのオープン一 今回の HeX て、は , ウインドウのリサイズは行わない という方針て、す。これは , コンストラクタ て、 Attr というメンバ変数を設定することに より実現て、きます。 Attr は , OWL がウイン ドウを Create するときに参照する構造体 て、 , ウインドウのスタイルや大きさ , メニ ューなどの情報を格納て、きます 0TWindow クラスのコンストラクタは , 適当なデフォ ルト値を Attr に設定しています。よって , TWindow クラスの派生クラスて、は , 変更し たいものだけ設定すれば OK て、す。 HeX の場合 , Attr. StyIe & = WS THICKFRAME ; Attr. StyIe ー =WS BORDER ; Attr. StyIe & = WS MAXIMMIZEBOX ; によって , WS THICKFRAME をやめて W S BORDER( 細い実線の枠 ) にし , さらに最 大化ボタンとシステムメニューの最大化を 98 C MAGAZINE 1993 5

7. 月刊 C MAGAZINE 1993年5月号

目が追加され , 基本クラスのポイ fflush (fp) ; ンタ変数を使ったときて、も仮想関 などとしてバッフアの内容をフラ 数テープルを使ってオプジェクト ッシュしてから setftime を実行する にふさわしい関数を呼び出すこと ようにしてください がて、きます。 デストラクタも , これと同じて、 Q す。デストラクタが仮想関数とし Borland C 十十を使ってアプ て定義されていないと , 基本クラ リケーションを開発したのですが , スのポインタ変数に対して delete を ランタイムライプラリや ObjectW 実行してもオプジェクトにふさわ indows などのライプラリを DLL と しいデストラクタを呼び出すこと してリンクしています。このため , がて、きません。 アプリケーションを販売したり配 布するときには BC30RTL. DLL や OWL. DLL なども必要になります。 Q f0P00 を使 0 てオープンしたフ これらを配布するためには , 何か ァイルに対して , 契約が必要ですか。 setftime (fileno (fp) , &ft) ・ A BC3()RTL. DLL, OWL. DLL, としてタイムスタンプを設定して いるのですが , fclose を実行すると TCLASS. DLL, BWCC. DLL は , 設定されたタイムスタンプが失わ 「 BorlandC 十十て、開発したアプリ れてタイムスタンプが現在の時刻 ケーションとともに配布する」とい になってしまいます。 う条件のもとて、 , 特別な契約なし に配布することがてきます。その A setftime はタイムスタンプ ( フ 際 , DLL の著作権表示を削除した ァイルの最終書き込み時間 ) を指定 り , ファイルの改変などを行わな された時間に設定しますが , その いて、ください。また , DLL ファイ 後にファイルに対して書き込みが ルを単体て配布することはて、きま 行われると設定した内容は失われ せん。なお , BGI ファイルも同様の てしまいます。高水準入出力関数 扱いとなります。 (fopen など ) て、は , ファイルへの書 ※本誌 ' 93 年 1 月号の本コーナーに Q 一般に , 基本クラス型へのポイ 掲載されたクラスのメンバ関数 き込みはバッファリングされてい クラスを継承させています。 ンタ変数には派生クラスのオプジ るのて、 , fclose の直前て、 setftime を の制約表に関して誤りがござい 基本クラスへのポインタ変数に派 ェクトのアドレスを代入すること 実行しても fclose によってバッファ ました。コピーコンストラクタ 生クラスのオプジェクトへのポイ がて、きます。このポインタ変数を の内容が書き出されてファイルの の項目の追加と = 演算子の訂正 ンタを代入したのですが , delete 使ってメンバ関数を呼び出すとき は TabIe 1 のとおりて、す。ご迷惑 を実行しても基本クラスのデスト タイムスタンプが更新されてしま は , そのメンバ関数が仮想関数と うこともあります。こうした場合 をおかけした皆様にお詫び申し ラクタしか呼び出されません。派 して定義されていなければ , 基本 上げます。 生クラスのデストラクタを呼び出 は , クラスのメンバ関数が呼び出され させるためには , どうすればよい TabIe 1 クラスのメンバ関数の制約表の追加 / 訂正 てしまいます。仮想関数が定義さ のでしようか。 仮想関数 メンバ関数か テフォルトで 継承 戻り値 での定義 生成されるか フレンド関数 れている場合 , そのオプジェクト A 基本クラスのデストラクタを コピーコンストラクタされない 不可 なし メンバ関数 される には暗黙のうちにクラスの仮想関 = 演算子 されない 可 あり 両方 されない 仮想関数 (virtual) として宣言して 数テープルへのアドレスを指す項 メニュー情報を構築 1 : TMenuBar *TMyApp: : initMenuBar( TRect r ) 3 : / / 下端を「上端の 1 行下」に移動 r. a. y 十 1 : 4 : return nev TMenuBar ( r, *new TSubMenu("-F-ile ” , kbAltF) + 5 : 6 : *new TMenuItem(" 0 pen ” cmMyFil e0pen, kbF3, hcMenu0pen, *nev TMenuItem(" N-ev 7 : cmMyNevWi n, kbF4, hcMenuNev, ” F4 ” ) + 8 : nevLine() + *new TMenuItem("E-x-it" 9 : cmQu i t, cmQui t, hcMenuExi t, ” GRPH-X") + *new TSubMenu ("-w- indOW ' kbAltW) + *new TMenuItem(" N-ext" cmNext, kbF6, hcMenuNext, z ー 00 田 *new TMenuI tem( ” cm Zoom, kbF5, hcMenuZoom, "F5") 13 : RAW モードで標準入出力を行う List 5 1 : # i nc lude く s td i 0. h > 2 : # i nc lude く i 0. h> 3 : #define RAW_MODE 0X0020 4 : #define IOCTL-GETMODE 0 5 : #define IOCTL_SETMODE 1 6 : main() 8 : 9 : 16 : 20 : 22 : } i n t C : int stat; setbuf(stdin, NUL い ; (unsigned char)ioctl(fileno(stdin), IOCTL_GETMODE) ; stat / / 標準入力デバイスを RAW モードに設定する。 ioctl(fileno(stdin), IOCTL_SETMODE, (void * ) (stat ー RAW_MODE)) : / / ' q ' を押すまで繰り返す。 〃 ( 標準入力は、テキストモードなので復帰 (0x0D) は無視される ) vhile ()c = fgetc(stdin)) ! ニ printf( ” input char = %c%n ” / / 標準入力デバイスを元に戻す。 ioctl(fileno(stdin), IOCTL_SETMODE, (void *)stat) : return 0 ; lnformation from Compiler Makers 155

8. 月刊 C MAGAZINE 1993年5月号

C 言語プログラミングレッスン List 9b はずて、す。 100 個だと画面が上に流れていっ 全角文字 ( 漢字 ) て、入力してはいけませんよ。 のひとって、す。そこて、 , ソースプログラム てしまいますが , 20 個なら一画面に収まり 修正したらファイルを保存します。これ を修正する方法についてお話しておきまし て、ソースプログラム NUMBERS ℃があなた ますね。 プログラムの修正と再コンパイルは以上 の手によって修正されました。これが「プロ 修正するにはエデイタが必要て、す。ェデ グラムの修正」て、す。ソースプログラムは修 のように行います。うまくいきましたか。 イタはあなたの使い慣れたものて、かまいま ぜひ自分の手て、「やってみて」くださいね。 正されましたが , その修正結果はまだ NUM せん。 SE3 を使うなら , NUMBERS ℃を自分て、いろいろ修正しては BERS. EXE には反映されていません。あな A> SE3 NUMBERS ℃ コンパイルし , 実行してみてください と入力します。修正するのはあくまて、ソー たの修正を実行ファイル NUMBERS. EXE くら間違いをやらかしてもコンヒ。ュータが に反映させるにはどうしたらよいて、しよう スプログラム (NUMBERS. C) て、す。実行フ 壊れることはありませんから安心してトラ ァイル (NUMBERS. EXE) を修正するわけて、 か。そう , もう一度コンパイルする必要が イしてみてください あります。はじめにコンパイルしたときと はありません。 たとえば , 表示する数を 0 ~ 99 の 100 個て、 同じように ササイズの解答 はなく , 0 ~ 19 の 20 個にしてみましよう。ソ A> LCC NUMBERS ℃ と入力します。コンパイルが終了すると , ースプログラムのはじめのほうを見ると , 先ほどの出題の解答て、す。以下を読む前 ファイル NUMBERS. EXE がて、きているはず 「 MAX NUMBER が 100 のとき , 0 ~ 99 まて、 に , 必ず自分のカて、解いてみてください て、す。 を表示する。」というコメント ( 注釈 ) があり , 0 間違ってもいいて、すから , とにかく自分の A > D 旧 N U M B E RS. * #define MAX NUMBER 100 頭て、一回は考えてから解答を読んて、くださ と入力して , NUMBERS ℃の時刻よりも N という行があります。ここの 100 を 20 に書き UMBERS. EXE の時刻カ噺しい ( 同じて、もよ いいて、すか。 換えれば表示する数が変化します。工ディ 0 問題 1 の解答は List 8a にあります。 3 桁て、 い ) ことを確認しましよう。いよいよ実行て、 タを使って 桁揃えするには % 3d と書けば OK てす ( List 8 す。 #define MAX NUMBER 20 a の実行結果は Fig. 8 ) 。 と修正してみましよう。かな漢字変換ソフ 間題 2 の解答は List 9a にあります。公式に トはオフにしてありますね。 20 という数字を 問題 1 の解答 0 0 0 A > N U M B E R S これて、表示される数の個数は 20 個になった 問題 2 の解答 1 : #include く stdio. h> 2 : 3 : 聞ⅲ ( ) printf( ” Xd*n ” , ( 2 + 3 ) * 4 / 2 ) : 5 : List 9a List 0 1 00 れ 0 4 ′ 0 6 叮ー 8 9 0 -1 り 00 4 - -0 6 7 ・ 8 9 1 っ 0 00 ^ 0 っ 0 CO 介 0 00 っ 0 っ 0 00 んⅣんⅣん % ⅣんⅣんⅣんⅣんⅣんⅣんⅣん 0 1 り朝っ 0 4 ′ 0 6 7 8 9 0 0 ・ 1 0 1 00 介 0 -4 【 0 6 ー 8 9 0 1 0 ム 00 4 一 -0 6 7 8 9 0 1 つなり 0 4 0 6 1 ム 1 1 1 1 1 1 問題 2 の解答の八イグレード化 1 : #include く stdio. h> 2 : 3 : int daikei(int a, int b, int h); 4 : 5 : main() printf( ” Xd*n ” , daikei(), 3 , 4 ) ) ; 7 : 9 : 10 : / * 関数 daikei は台形の面積を求める関数である。 * 引数 a は上底の長さ、引数 b は下底の長さ、 11 : * 引数 h は高さである。 12 : 13 : * 台形の面積を求める公式は、 * 面積 = ( 上底十下底 ) X 高さ + 2 14 : * である。 15 : 17 : int daikei(int a, int b, int h) 18 : { return()a + b) * h / 2 ) : 19 : 問題 1 の解答のハイグレード化 Li st 8b 1 : #include く stdio. h> 3 : main() int i; 5 : 6 : for (i = 0 ; i く = 10 ; i + + ) { 7 : printf( ” X2d X X2d = X3d*n", 8 : 9 : C 言語入門講座 89

9. 月刊 C MAGAZINE 1993年5月号

にも多いのて、 , 本題に入る前に私に与えら れたページ数をオーバしてしまう。残念な がらほかの機会に譲り , こて、は説明を割 愛する。 本誌の読者て、あれば , コンパイラにおけ るエラー表示の重要性はいまさら説明する まて、もないかもしれない。構文上の誤り ( セ ミコロンがないなど ) や意味上の誤り ( 引数 の数が適合しないなど ) をチェックして , プ ログラマに知らせることがエラー表示部の おもな仕事て、ある。 これらは , コンパイラの入力となるソー スプログラムの誤りてあるが , コンパイラ が扱うべき誤りはもうひとつある。それは , コンパイラ自身の誤り ( コンパイラのバグ ) てある。コンパイラほどの複雑なプログラ ムて、は , バグがひとつもないという状態は 考えにくい。このため , コンパイラが正し く動作していることを確認するためにも , コンパイラ自体を用いるのがよい。 CPL/O には , そのために 2 種類の機構が組み込んて、 ある。 そのひとつは , コンパイラ自身が行う自 己診断て、ある。この例として List 20 を見て ほしい ogen 0P1 は「単項演算子」のコード生 成を行う関数て、ある。 CPL / 0 の場合 , 単項 演算子は , 十と一の 2 種類だけだから , それ 以外の演算子が単項演算子として使われて いたら , 文法工ラーて、ある。しかも , この 文法工ラーは構文解析部て、チェックしてい るため , gen 0P2 の引数として , 十やー以外 の演算子を受け取ることはありえない。そ のありえない引数がきた場合はコンパイラ のエラー ( バグ ) と考えられるのて、 , 積極的 にエラーを表示しているのて、ある。 関数 system error はコンノヾイラ自身の工 ラーを表示するための関数て、ある。上記は 比較的単純な例だが , 同じような趣旨のチ ェックが都合 10 か所ほど入っている。これ らはバグをて、きるだけ早期に発見するため プロトタイプ宣言の自動切り換え が , それて、もコンパイラのように大量かっ 最近はデバッガの機能が高くなってきた 仕事て、ある。 きとめ , それを修正するのはプログラマの 果バグがあるとわかっても , 真の原因をつ い場合も多いだろう。また , 自己診断の結 るから , 人間がチェックしなければならな しかし , 機械的な自己診断には限界があ ノヾッグ情報 ssert ( 0 ) を用いてよい理由を説明せよ [ 問題 ] 関数 system error の代わりに a わりに , assert ( 0 ) を用いてもよいだろう。 はないから , system_error という関数の代 しなければならないと決まっているわけて、 も , assert マクロだって , チェックをオフに もっと とは永遠にいえないはずだからだ。 う自己診断ルーチンを取り除いてよいなど バグは忘れたころにやってくるから , も ム完成後も取り除かない るのに対して , 前記の自己診断はプログラ れる ( リスト上は残るが ) ことが一般的てあ LiSt 自己工ラー診断の例 複雑なデータを扱う場合には , デバッガだ けて、データを調べることは容易てはない このため , CPL/O には , デバッグ情報を表 示する機能が組み込んて、ある。 こて、 , もう一度 List 19 を見てほしい この関数の冒頭付近に , 「 % がソース中にあ ると , 記号表の一覧を表示する」とあるが , これがデバッグ情報を表示するための機能 て、ある。同様の機能として , コード生成し た結果を表示するための「逆アセンプラ」も 用意している。 一般に , デバッガて、データを確認するこ とは , 顕微鏡による検査にたとえることが て、きるだろう。細かくデータを追うことは て、きても , プログラム全体をざっくりと見 渡すことにはデバッガは向かない。このた め , 「木を見て森を見ず」という状態になり やすい。これを補うために , 要所要所て、適 格なデバッグ情報を表示て、きるようにして おくと , デバッグが非常にはかどる。 自己診断と同様 , デバッグ情報の表示部 設計の段階から , デバッグがしやすいよ うに配慮しよう 2 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : 13 : List 5 : #end i f 4 : #define D(A) 3 : # se 2 : #define D(A) 1 : $ifdef -_STDC_ 1 : VOid gen-opl( 叩 ) int op; svitch ( 叩 ) { case op-add ・ break; case op_sub gen(f-opr, 0 , break, defaul t : system_error(' gen_opl break; v-neg) ; / * 単項オペレータ * / / * 演算子の種類 * / / * 単項 + の場合はなにもしない * / / * 単項ーのコード生成 * / / * それ以外はありえない。工ラー * / の設計て、ある。 72 C MAGAZINE 1993 5 チェックは , デバッグ終了時には取り除か assert マクロがあるが , assert マクロによる これと似た発想として標準ライプラリの 6 : 7 : 9 : VOid enter-constant D((symbol_t * , inti i n t ) ) : 8 : VOid enter-variable D((symbol_t * , int)); VOid release-idents D((ident-t * ) ) :

10. 月刊 C MAGAZINE 1993年5月号

計一ま 設技 : 「一 特集 C プログラム 状態推移表 る。これまて、のプログラムに比べて , 少々 推移図の記法て、あるバブルチャートと呼ば 文字列 小道具が増えているのて、 , まずその解説を れる図式を用いている。各バブルに付され 終了 ている SO ~ S3 , OK などの名前は「状態」を識 しておこう。最初に enumcharKind という SO S2 列挙を宣言し , それを charKind t という名 別するためのものて、ある。名前て、なくても , S2 前て、 typedef している。これは文字の種別を 整数て、もかまわない ( 後の例て、はプログラミ 表すためのものて、ある。 10 進整数をチェッ ング上の関係から整数にする ) 。各状態にお S2 クする用途て、は , 文字は空白類 , 符号 , 数 いて , 定義された入力が与えられると , 特 字 , 文字列の終わり , そしてそれ以外とい 定の状態へと推移する。そして一般には , う 5 種類に大別して考えることがて、きる。 その「推移する」瞬間に仕事を行う・・ CharacterKinds [code] れは ctype. h をインクルードして ,isdigit() えば何らかの値を出力するのて、あるが , 今 などを利用してもかまわないが , ここて、は として参照すれば , その文字コードに対す 回のプログラムては , 最終的に「与えられた 文字列は構文に沿っているかどうか」を判断 抽象化をねらって列挙を使用している。そ る charKind t の値が得られるようにするた して , 文字コードの数と同じ要素数を持っ めのて、ある。ややメモリがもったいない感 するだけて、よいのて、 , 途中て、は仕事をする charKind t 型の配列 CharacterKinds [ ] を じもあるが , たかだか UCHAR MAX ( 普通 必要がないこともあり , 論点をばやけさせ 宀言する。これは任意の文字コードが与え は char は 8 ビットなのて、 , 全部て、 256 要素 ) ないためにそれに関しては配慮していない られたときに , その文字コード - を添字として , なのて、 , このような方式を採用した。 さて , この状態推移図を素直にプログラ 状態推移表による check int の実装 0 1 っム 3 S3 S3 OK OK 0 List 5 List 52 : 53 : 54 : static int st0 55 : stat i c i nt st 1 56 : static int st2 57 : static int st3C 58 : 59 : / * 60 : * 状態推移行列 62 : int *StateMatrix ロ 63 : st0, stl, st2, st3, 64 : } ; 65 : 66 : / * 67 : * 状態推移解釈ルーチン 68 : 69 : static int 70 : stateMachlne(charKind-t *kinds, int **matrix, const char *nptr) i n t st : 73 : for ()t = 0 : st ! = OK; + + nptr) if ((st = matrix[st)[kinds[*nptr]]) = ERR) return 0 : 76 : return 1 ; 78 : } 80 : / * 10 進整数文字列のチェックを行う 82 : int check-int(const char *nptr) 83 : 8 4 : { return stateMachine(CharacterKinds, StateMatrix, nptr) ; 85 : 86 : } 87 : 88 : #i fdef TEST 89 : #include く stdiO. h> 90 : int main(int argc. char **argv) if (argc く 2 ) return 0 : initKinds(CharacterKinds) : 95 : ⅶ ile (* + + argv ! = NULL 96 : printf("%s = %d%n", *argv, check-int(*argv)); return 0 : 100 : #end i f ・ーワ朝っ 0 っロ】 0 「】ワひワ 0 1 : # i ncl ude <l ⅳ i ts. h> 2 : 4 : * 文字の種別を表す列挙 6 : typedef enum charKind { 7 : Eos 8 : White, 9 : Sign, 10 : D i g i t, Point, Exponent, 12 : 0ther, 14 : } charKind-t; * 各文字コードに対応する文字種別を記憶する配列 19 : charK i nd-t CharacterKinds[UCHAR-MAX] : 20 : 22 : * 文字種設定用補助関数 23 : 24 : static VOid setKinds(charKind_t *kinds, char *s, charKind-t kind) 26 : kinds[(unsigned char)*s + + ] ニ kind : 27 : 29 : 30 : / * * 文字種配列初期化関数 32 : 33 : static VOid initKinds(charKind-t *kinds) 34 : { 35 : i n t i : 36 : kindsC'%0'] = Eos; i く UCHAR_MAX + 1 : + + i ) fo 「 (i 38 : kindsCi) = 0ther; 39 : " 0123456789 " , Digit); setK i nds(kinds, 40 : Sign); setK inds (kinds. ”十一 setKinds (kinds, Point); setKinds(kinds, ” eE ” . Exponent) : 43 : White); setKinds(kinds, ” }t}fYn ” 44 : 46 : 47 : #define ERR ー 2 48 : #define OK 50 : / * * 各ステートに対する状態推移データ 特集 C プログラム設計技法 53