( Ⅱ ) ヒープ はなく静的データ領域に格納されるのて、 , スタックチェックを行う関数が用意されて func 関数を呼び出した段階て、も , スタック において , (A), ( B ) がそれぞれ最大 64K バ おり , それを呼び出すことになる ) 。ここて、 の消費は 16K バイト十て、済むことになる。 イトに収まらなければならない。 (B) の中て スタック領域が不十分て、あることがわかれ 静的データ領域にさえ余裕があれば , 配列 ( I ) は , プログラム開始時に大きさが決ま ば , 工ラーメッセージを表示してプログラ の定義を関数の外に出すだけて、 , スタック っているのて、 , もしこの領域が 40K バイトて、 ムを終了する。ただし , 処理系によっては , オーパフローを回避て、きるのだ ( プログラム あれば , ( 0 ) と ( Ⅱ ) の合計が 24K バイトに収 このチェックを行わなかったり , コンパイ の意味も変わってくるが・・・ まらなければならないことになる。 ルオプションによってチェックの ON / OFF さて , スタック領域の大きさだが , 処理 を制御て、きるものもある。スタックオーバ タヅクオ勺 ( フロー検知のメカニズム 系 , 実行環境 , 選択するメモリモデルなど フローのチェックは , 各関数が呼び出され によって異なる。処理系によっては , リン 有限て、あるスタックを消費しつくしたと るたびに毎回行われるのて、 , チェックを行 ク時にスタックの大きさを指定て、きるもの きに , スタックオーノヾフローのエラーが発 わければ , 実行速度は向上する。しかし , もある。具体的なことは , お持ちの処理系 生することはわかっただろう。それて、は , スタックが不足しているにもかかわらず , 具体的には , どのようにしてエラーを検知 のマニュアルを見ていただきたい 処理を続けるのは , 非常に危険だ。暴走す なお , 一般の 8086 用コンパイラて、のスモ するのかというメカニズムについて簡単に る可能性すらある。 ールモデルを簡単に説明すると , 解説しよう。 【重要】完全に開発およびデバッグが (A) ( Ⅳ ) コード 各関数の最初に , スタック領域が十分て、 終了するまて、は , スタックオーパフロ (B) ( 0 ) スタック + ( I ) 静的データ十 あるかどうかのチェックを行う ( 一般には , ーのチェックを外してはならない プログラムとスタック スタックを破壊した - 難しい話はさておき , おもしろいプログ ラムを示そう。まず , List 7 だ。 func 関数中 て、定義された配列 x は 4 文字分の大きさを持 つ。ここて 9 行目の , memset (), O, 8 ) ; によって , 配列の領域を越えて x [ 0 ] ~ x [ 7 ] の 8 文字分に無理矢理値を書き込んて、みる。 もちろん , 処理系によっても動作が異なる main 関数に戻らないプログラム ( 正常終了 ) 0 List い 8 」いいに」に」りいにをー「いいー . z [ 8192 ] : 2 : 3 : void func(void) int x [ 8192 ] ; 8 : int main(void) int a [ 8192 ] : 10 : func() : / * 1 6 K パイト * / int / * 16 K パイト ーいにに ( リリーをいに一いにい - / * 1 6 K パイト * / main 関数に戻らないプログラム ( 異常終了 ) LiSt List いーい・ 1 : # i ncl ude く stdio. h> 2 : #include く string. h> 4 : void func(void) xC4]; char 7 : puts("func 関数の中です” ): 8 : memset(), 0 , 8 ) : 10 : } 12 : int main(void) 16 : 18 : 19 : } 1 : #include く stdio. h> 2 : #include く string. h> 3 : 4 : vo i d func ()o i d) puts("func 関数の中です "): 6 : asm add sp, 2 7 : 9 : 10 : int main(void) 12 : 14 : 15 : 16 : 」ににいリりリリリい」にいに朝いににいいいい一」ににに puts ( " func 関数を呼び出します " ) : func(); pu ts ( " func 関数から戻ってきました " ) : puts ( " func 関数を呼び出します” ) : func(); puts ( " func 関数から戻ってきました " ) ; return ( の : return ( 0 ) : 116 C MAGAZINE 1 3 5
ープロクラミング道場 Dr. 望洋の て、あろうが , Turbo C Ver. 2 て、は , Fig. 4 に示すように , プログラムは main 関数に戻 ることなく異常終了する。スタック上の配 列 ( 関数の中て、定義された自動的記憶寿命を 持っ配列 ) の領域を越えたアクセスは , スタ ック上に保持されているリターンアドレス を破壊する可能性もあるのだ。 次に作成した , もっとおもしろいプログ ラムが List 8 だ ( このプログラムは , スモー ルコードモデルを前提としている ) 。 ん nc 関数からのタンアトス このプログラムて、は , func 関数にも main ーのリタ←ンドス 関数にも , 引数や自動的記憶寿命を持つ変 数はない。したがって , スタック上には , とや , 個人的な質問には一切お答えて、きな このような状況て、あれば , 本当にスタッ main 関数からのリターンアドレスと func 関 ク領域が不足していると考えられる。 ことをあらかじめご了承されたい 数からのリターンアドレスのみが格納され ( 2 ) printf 関数の呼び出しでのみスタック どんな小さな疑問点て、も結構なのて、 , 気 ている (Fig. 5 ) 。 オーパフローが発生する 後れせずに , 応募していただきたい ( ただ func 関数て、は , Turbo C のインラインア し , 特殊な題材よりも , 一般性の高い題材 まず , printf 関数に渡す引数が多く , たま センプリの機能を用いて , ちょっとしたイ を優先的に取りあげることを , あらかじめ たまスタックオーパフローが発生するとい タズラを行っている ( 7 行目 ) 。 うことが考えられる。そのほかに , 以下の ご了承願いたい ) 。 asm add sp, 2 ような原因が考えられる。 ( 1 ) 氏名・住所・電話番号 によって , スタックポインタを無理矢理書 ②匿名希望の有無 / ペンネーム ・ printf 関数の利用法が間違っている き換えている。この操作によって , func 関 ( 3 ) 質問・相談事項 ( なるべく具体的に ) ・文字列領域が破壊されている 数から戻ろうとするときのスタックポイン 宛先 printf 関数を呼び出すときは , ほとんどの タは , func 関数からのリターンアドレスて、 〒 108 東京都港区高輪 2 ー 19 ー 13 場合 , はなく , main 関数からのリターンアドレス printf()x = %d*n", x) ・ NS 高輪ビル を指してしまう。プログラムは , func 関数 ソフトバンク ( 株 ) 出版事業部 のように , 第 1 引数として文字列リテラルを から main 関数に戻るつもりなのだろうが , C マガジン編集部 渡す。文字列リテラルは , 静的記憶寿命を 実際には main 関数の戻るべきところに戻っ 『 Dr. 望洋のプログラミング道場』係 持っため , 固定データ領域上に格納される。 てしまう。すなわちプログラムは終了して もし , この領域が破壊されていたら , printf しまうのて、ある。 Fig. 6 に実行結果を示して ・参考文献 関数は正常に動作しないて、あろう。これが いるように , 工ラーが発生することもなく [ 1 ] 平林雅英 , fANSI C 言語辞典』 , 技術評 引き金となって , スタックオーパフローが ( func 関数から main 関数に戻ることもなく ) 論社 , 1989 発生することもあり得る。 プログラムは無事終了している。 [ 幻柴田望洋 , 『秘伝 C 言語間答ポインタ p 面関数 0 出レ 問募集 編』 , ソフトバンク , 1991 スタックオーノヾフロー [ 3 ] 柴田望洋 , ℃言語プログラミング ~ デ ータ構造とアルゴリズムの設計 ~ 暫定 本連載て、は , 読者からの質問にお答えし , 村山さんの相談て、は , printf 関数の呼び出 版』 , 私家版 ( 九州大学工学部化学機械 また希望に応じてプログラムの添削も行う。 し時にスタックオーパフローが発生すると 工学科第 7 講座内部資料 ) , 1992 右記必要事項を明記の上 , フロッヒ。ーディ のことだ。具体的な状況がわからないのて、 , スクにて応募していただきたい ( 短い質問て、 いろいろな場合を推測してみよう。 あれば , 書面のみて、可 ) 。なお , 応募いただ しばたばうよう ( 1 ) printf 関数以外の関数を呼び出しても いたフロッピーディスクは返却て、きない ( 工学博士・九屮大学工学部化学機械工学科・ ) スタックオーパフローが発生する Fig. 6 List 8 の実行結果 A>LlST8U func 関数を呼び出します func 関数の中です A> Fig. 4 List 7 の実行結果 A>LlST7U func 関数を呼び出します func 関数の中です Abnormal program termination A > Fig. 5 List8 のプログラムて func 関数が呼び出されているときのスタックの状態 スタックポインタ - ・一一一一一一 - → Dr. 望洋のプログラミング道場 117
次々とブッシュされ , その関数から戻ると 引数 関数の呼び出 きにスタックからポップされるのだ。 関数内で定義された自動的記憶寿命を List 4 のプログラムを考えよう。 main 関 持つオプジェクト 関数の呼び出しとスタック 数は , c 関数を呼び出す。その c 関数は , a 関 がスタック上に蓄えられるのて、ある ( ただし 数と b 関数を順次呼び出す。プログラムの実 関数呼び出しの際に , スタックが利用さ レジスタ上に保持されるものを除く ) 。もち 行開始から終了まて、の関数呼び出しの手順 れることはわかったが , 具体的にどのよう ろん , 関数から戻るときは , これらのデー を示すと , 次のようになる。 なものがスタック上に蓄えられるのかを考 タはスタック上から除去される。 ( 0 ) List 5 を例に , 関数呼び出しにおけるスタ ( 1 ) main( ) 呼び出された関数から , 呼び出し元の関 ックについて具体的に考えよう (Fig. 3 ) 。 ② main ( ) → c ( ) 数に戻るときは , その関数を呼び出した場 多くの C 言語コンパイラは , 関数呼び出し ( 3 ) main ( ) → c ( ) → a ( ) 所 ( 厳密にはその直後 ) に正しく戻ってこな の際に , 後ろのほう ( 右側 ) の引数から評価 ( 4 ) main ( ) → c ( ) ければならない。たとえば , 先の例て、は , 関数呼び出しとスタック ⑤ main ( ) → c( ) → b( ) a 関数終了後は , List 4 の 15 行目直後に戻る ( 6 ) main ( ) → c ( ) 必要がある。したがって , 一般にリターン ( 7 ) main( ) アドレス (return address) , すなわち関数終 了後に戻るべき番地を保存するのて、ある。 ( 8 ) こまて、示せば , 著者のいわんとするこ a 関数を詳しく考えよう。この関数には , とがわかるだろう。 ( 0 ) ~ ( 8 ) は , 現在呼び 引数 int 出されている関数のリストて、あるわけだが , 自動的オプジェクト int これは見事にスタック形式となっているこ a [ 10 ] ; int とが見て取れるはずだ の変数が使われる。これらの変数は , この たとえば ( 3 ) は , a 関数が呼び出されてい 関数が実行されている間のみ『生きて』い る状況を示している。そして , a 関数から c ればよい , 自動的記憶寿命を持つ。したが 関数に戻った状態が ( 4 ) て、ある。 ( 5 ) は , そ って , これらのデータもスタックに保存す の後に b 関数を呼び出した状態だ。すなわ れば都合がよい。つまり , ち , 呼び出された関数がスタックに対して リターンアドレス 関数呼び出し Fig. 3 関数呼び出しとスタック List ー func 関数 2 : void func(int a, long b, char *c) X ; 5 : 9 : 10 : / * - ー main 関数 int main(void) int int 15 : Char 17 : func(a + 3. b, c) : 18 : 19 : return ( 0 ) : i n t ー ong List 1 ーっワ 0 4 ・ LO っ叮ー 8 0 , 1 り 0 00 ・ -. 0 c_D ー 8 0 ーーワ】っ 0 ・ー・ 11 1 ー 14 ーー 1 よ 1 よー人 1 一 1 ーっ朝り 0 り乙 int a=O; int b=2; char c[]= ” ABC ” func(a 十 3,b,c) 未使用工リア x, y などの 自動的オプジェクト リターンアドレス 3 (int 型 ) 2 (long 型 ) &cCO] (char* 型 ) 使用中のスタック領域 int a [ 10 ] ; VOid func(int a, c) long b, Char int x; long y, 114 C MAGAZINE 1993 5
List 7 1 18 : 120 : 121 : 122 : 1 2 3 : 124 : 125 : 126 : 128 : 129 : 130 : 131 : 132 : 133 : 134 : } 135 : 137 : 138 : 139 : 140 : 141 : 142 : 143 : } 1 4 4 : 146 : 147 : 148 : 149 : 150 : 1 5 1 : 1 5 2 : 153 : 154 : 1 5 5 : 156 . 159 : { 160 : 1 6 1 : 162 : 163 : 164 : 165 : 166 : 167 : } 168 : 169 : 170 : { 171 : 172 : 173 : 174 : 175 : 176 : } 178 : 179 : 180 : 181 : 182 : 183 : 184 : 185 : 186 : 188 : 189 : 190 : 191 192 : 194 : { 195 : 196 : } 197 : 198 : / * 199 : 200 : 201 : 203 : 204 : 205 : 206 : 207 : 208 : 209 : 210 : 21 い return (funcp-t)st3; case Exponent: return (funcp-t)NULL; return (funcp-t)st4; statiC funcp-t st4(charKind-t kind) switch (kind) { case S ign: return (funcp-t)st5; case D i g i t : return (funcp-t)st6; return (funcp-t)NUL し : 136 : static funcp-t st5(charKind-t kind) switch (kind) { case D i g i t : return (funcp-t)st6; return (funcp-t)NULL; 145 : static funcp-t st6(charKind-t kind) svitch (kind) { case EOS : return (funcp-t)ok; case White: return (funcp-t)st7; case D i g i t : return (funcp-t)st6; return (funcp-t)NULL; 158 : static funcp-t st7(charKind-t kind) svitch (kind) { case return (funcp-t)ok; case Whi te: return (funcp-t)st7; return (funcp-t)NULL; static funcp-t st8(charKind-t kind) svitch (kind) { case D i g i t : return (funcp-t)st9; return (funcp-t)NU しし static funcp-t st9(charKind-t kind) svitch (kind) { case Eos: return (funcp-t)ok; case White: return (funcp-t)st7; case Digit: return (funcp-t)st9; case Exponent: return (funcp-t)st4; return (funcp-t)NULL; 193 : static funcp-t ok(charKind-t kind) return (funcp-t)NULL; * 状態推移解釈ルーチン static int return 1 : return 0 : if ()t = (funcp-t (*)(charKind-t))NUL い st = (funcp-t (*)(charKind-t))st(kinds[*nptr]) : for ()t = s0; st ! = 0k; + + nptr) { funcp-t (*st)(charKind-t) : 202 : stateMachine(charKind-t *kinds, funcp-t s0(charKind-t), const char *nptr) ロ 技法 めには , もっと広いクラスを受理て、きるモ デルが必要になる。そのような場合には , 状態推移図をネストさせるという対策を施 すことがて、きるのだが , 本稿て、はこれ以上 は触れないことにする。 状態推移モデルの明白な弱点として , ソ ースを見ただけて、 , どのような処理を行お うとしているのかが一見て、は判然としない 点が指摘て、きる。 List 1 や List 3 て、あれば , ソースを注意深く読めば , だいたい何を意 図しているのかが理解て、きよう。 ところが , List 5 や List 6 て、はそうはいかないだろう。 したがって , このアプローチを用いる場合 には , 必ずドキュメントに状態推移図をつ けておくことが重要て、ある。それをしない と , このコードはもっとも保守がめんどう なコードということになりかねない 最後に ,List 5 や List 6 に見られるアプロ ーチを用いると , 処理速度的には不利にな る場合もあることも記憶しておいてほしい 状態推移部分は , 一種のインタブリタを形 成しているからて、ある。もっとも , 多くの アプリケーションて、はこのペナルティは許 容範囲内て、ある。 以上見てきたのは , ソフトウェア全体の 設計の話て、はなく , 単にひとつの関数の話 にしかすぎない。しかも処理的には比較的 簡単だと思われる浮動小数点数文字列のチ ェックプログラムなのて、ある。それなのに たったこれだけの関数にかぎっても , 実は さまざまなアプローチが可能て、ある。そし て , それぞれのアプローチには長所短所が 存在しており , 「こういった状況の下て、は , この方法が優れている」というようなことは いえても , 常にどれかがいちばん優れてい るとはいえないのが実状て、ある。 この多用性がプログラム設計の難しさの 原因にもなっているのだが , 裏を返せばデ ザイナーの腕の見せどころて、もある。した がって , よいソフトウェアを設計するには , いろいろなアプローチ法を知っておく必要 がある。 良きデザイナーになるためには日々の胼 特集 C プログラム が重要て、ある。 特集 c プログラム設計技法 57
Li st 7 えなければならない部分がきっちりと分か れているからて、ある。 さて , 状態推移モデルの実装は List 4 に見 られる , 掟破りの goto 多用型と , List 5 や L ist 6 て、使ったような表参照型の , いわば状 態推移行列インタブリタ型と , この 2 種類し かあり得ないのかというと , もちろんその ようなことはない。同じモデルに立脚しな がらも , 異なる実装というのはまだほかに もある。 たとえば , ひとつの関数の中て、 goto を使 って制御を移す代わりに , 各状態にひとつ ずっ関数を割り当てて , それぞれの関数は , 入力として与えられた文字の種類に応じて , 「次に呼び出すべき関数へのポインタ」を返 すように作ることがて、きる。 List 7 が , このアイデアに基づいてコーデ イングしたものて、ある。関数へのポインタ を扱うところて、 , C 言語の型システム上の破 綻から , キャストを多用せざるを得ず , し かもかなり難解なキャストもあるのが難点 て、ある ( 型システムの破綻とは何かについて は , 残念ながらここて、触れている余裕はな い ) 。しかし , とにかく goto は出てこないの て、 , goto 有害論者の目をごまかすには , のようなコーディングがあることを知って おくのも悪くないだろう。 List 7 が List 6 のアプローチに比べてメリ ットがあるとすれば , それは , 各ステート がそれぞれ独立した関数になっているため に , デバッグコードを挿入するなど , 特定 のステートて、だけ , 何か特別な処理をする ことが容易にて、きるということて、ある。 状態推移モデルは , その実装をどう行う かはさておき , 文字列の解析処理にかぎら ず , 一般にイベントが離散的に発生し , そ の発生順序に従って何らかの処理を行わな ければならないような場合には有効なアプ ローチてあるといえる。それはもっと巨大 なソフトウェア全体の状態推移かもしれな いし , もっとミクロな処理て、あるかもしれ ただし , 先に述べたように , 正規文法て、 表現不可能な場合には , それを解釈するた 56 C MAGAZINE 1993 5 static funcp_t ok(charKind_t) : 68 : { 24 : 28 : 30 : 32 : 33 : 36 : 37 : 38 : 39 : 40 : 43 : 45 : 49 : 50 : 52 : 53 : 56 : 57 : 58 : 59 : 62 : 63 : 65 : 66 : 67 : 69 : 75 : 77 : 84 : 85 : 86 : 88 : 89 : 90 : 92 : 95 : 96 : 97 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : 108 : } 109 : 112 : 115 : 116 : kinds[i] = 0ther; ・ i く UCHAR-MAX + 1 : + + i ) for (i kindsC'%0'] = Eos; 34 : static V0id initKinds(charKind-t *kinds) * 文字種配列初期化関数 kinds[(unsigned char)*s + + ] = k ind; vhile ()s ! = ' \ 0 つ 25 : static V0id setKinds(charKind-t *kinds, char *s. charKind_t kind) setKinds(kinds, setKinds(kinds, setKinds(kinds, setKinds(kinds, setKinds()i nds, " 0123456789 ” , Digit); ”十一 Sign); P0int); ” eE", Exponent); ” %t%f%n ” , White) : * 各ステートに対する状態推移処理関数 typedef void *(*funcp-t) (charKind-t); funcp-t st0(charKind-t ki nd) static funcp-t st10(charKind-t) : static funcp-t st9(charKind-t): static funcp_t st6(charKind-t); static funcp-t st5(charKind-t); static funcp-t st4(charKind-t); static funcp-t st3(charKind-t) ; static funcp-t st0(charKind-t); 64 : static funcp-t stll(charKind-t); 61 : static funcp-t st8(charKind-t); 60 : static funcp-t st7(charKind-t); 55 : static funcp-t st2(charKind-t); 54 : static funcp-t stl(charKind-t); static return (funcp-t)st0; case Whi te: svitch (kind) { 93 : static funcp-t st2(charKind_t kind) return (funcp-t)NULL; return (funcp-t)st8; case Point: return (funcp-t)st2; case D i g i t : svitch (kind) { 82 : static funcp-t stl(charKind_t kind) return (funcp-t)NULL; return (funcp-t)st8; case Point: return (funcp-t)st2; case D i g i t : return (funcp-t)stl; case S ign ・ return (funcp-t)NULL; return (funcp-t)st4; case Exponent: return (funcp-t)st3; case Point: return (funcp-t)st2; case Digit: return (funcp-t)st7; case Whi te: return (funcp-t)ok; case EOS : svitch (kind) { 110 : static funcp-t st3(charKind-t kind) case D i g i t : return (funcp-t)st7; case Whi te: return (funcp-t)ok; case EOS : svitch (kind) {
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
ープロク、ラミング道場一 Dr. 望洋の ( Ⅲ ) は , 本来ならば ( 概念的には ) スタッ 整理しよう。 (a) 一般に , スタック領域の大きさは有 クに蓄えられるべきデータて、あるが , 高速 化のためにレジスタ上に保持されるものて、 限である (b) スタック領域は , ( main も含めて ) 関 ある。 数が呼び出されるたびに消費される ( Ⅳ ) に関しては , 他言を要しないて、あろ 1 回の関数呼び出しにおいて , 消費 (c) フ。 こて、 , それぞれの領域に対して , 以下 する大きさは , に示す名前を与えることにする。 ( 1 ) リターンアドレス ②引数 ( 0 ) スタック ( 3 ) 関数内で定義された自動的記 ( I ) 静的データ ( Ⅱ ) ヒープ 憶寿命を持つオプジェクト の合計である ( Ⅲ ) レジスタ 呼び出しのネストが深いときや , とくに ( Ⅳ ) コード 自動的記憶寿命を持っ変数 ( 配列など ) が大 これらは , 次のように大きくふたつに分 類て、きる ( ただし ( III ) はレジスタ上のものて、 いわゆるスタックオーパフロ きいときに ー (stack over-flow) の実行時工ラーが発生 ありメモリを消費しない特殊なものて、ある のて、除外する ) 。 する可能性がある。 それて、は , スタック領域には保持されな ・プログラムの開始時に大きさが固定され いものは何だろうか ? ているもの ( I ) 静的記憶寿命を持つオプジェクト ( I ) 静的データ ( Ⅳ ) コード ( 1 ) 関数の外で定義されたオプジェク ・プログラムの実行に応じて大きさが変化 を行い , スタックへブッシュする (ANSI て、 するもの は , 引数の評価順序は規定されていない ) 。 ( 0 ) スタック ( Ⅱ ) ヒープ ②関数の中で static を指定して定義さ List 5 の 17 行目の場合 , 最後の実引数は c て、 れたオプジェクト あり , 具体的には &c [ 0 ] の値がスタックに となる。 ( Ⅱ ) 動的記憶寿命を持つオプジェクト (m ブッシュされる。次に , 後ろから 2 番目の引 スタック領域とそのほかの領域 ( 2 訓 oc , ca c などで動的に確保する領 数て、ある b をスタックにブッシュする。ただ 域 ) し , b は int 型て、あるが , 仮引数 b の型は , 10 List 6 のプログラムを見ていただきたい ( Ⅲ ) レジスタ上に保持されるデータ ( リタ a, x, z の配列は , それぞれ 16K バイトの大 ng 型てあるため , 2 て、はなく , 2L へと変換さ ーンアドレス , 引数 , 自動オプジェ きさを持つ (int を 2 バイトと仮定している ) 。 れた値がスタックにブッシュされる。そし z は静的データ領域上に格納される。まず m クト ) て , 最初の引数て、ある a 十 3 , すなわち整数 ( Ⅳ ) コード ( すなわちプログラム ) ain 関数開始時は , スタックは , 3 がブッシュされる。最後に , リターンアド ( I ) はわかるだろうか ? 関数の外て、定 16K バイト ( 配列 a ) 十 レスがブッシュされる。 義されたオプジェクトや関数の中て static を 消費される ( このは , main 関数のリターン func 関数から , main 関数に戻るときは , 指定して定義されたオプジェクトは , プロ アドレスなどて、ある ) 。 main 関数から func 関 リターンアドレスとして格納された番地に 数を呼び出した時点て、あれば , グラムの実行を通じて , 常に『生きて』い 戻ることになる。また , このときスタック 32K バイト ( 配列 a と配列 x ) 十 なければならない。そこて、 , プログラムの の除去を同時に行うのて、ある。 開始から終了まて、 , ずっと固定した場所に 消費される。 スタックオーノヾフロー もし , スタック領域が 30K バイトまて、とい 蓄えられる。 ( Ⅱ ) に関しては , 最近本連載て、取りあげ う制限がある環境て、あれば , この時点て、ス てきたものだ ( 線形リスト , 2 分木など ) 。す タックオーパフローとなってしまう。 , スタック領域とそのほかの領域 ( 1) なわち , malloc や calloc などて、確保する領域 て、 , main 関数の中て、定義した配列 a を関数の 外に出して定義すれば , 配列 a はスタックて、 C 言語内部のスタックについて , もう一度 だ。 C 呼び出しと Pasc 引呼び出し とくにパソコン用の C 処理系では , PascaI 呼び出しといった属性を指定で きるものが多いようである ( 通常の関数 呼び出しのことを Pasc 引呼び出しに対 して C 呼び出しという ) 。 C 呼び出しで は , 関数を呼び出して , その後プログ ラムの流れが呼び出し元に戻った後で , 改めてスタックの除去を行う。 方 , PascaIA5' び出しでは , 呼び出 された関数は , スタックを除去しなが ら呼び出し元に戻るので , 若干動作が 速く , プログラムサイズも小さくなる というメリットがある。ただし , 省略 ' ' で表す可変個引数が使えな 言己万 ,... いといったデメリットがある。 ト Dr. 望洋のプログラミング道場 115
市販ライプラリのすすめ MS - Windows プログラム開発支援ツールズ 日 oboHELP ( ロボヘルプ ) WindowsMAKER Pr0fessionaIV4,0J 標準価格 . Y98 ′ 000 ( 税別 ) 標準価格 : 標準価格・ Y189 , 000 ( 税別 ) 対応機種 . 日本語 MS-Windows Ver3.0 以上が稼動する機種 対応機種 . 日本語 MS-Windows Ver3.0 以上が稼動する機種 〈特徴〉 〈特徴〉 ・ MS-Windows のヘルプファイルを作成支援するツールです。 ・ MS ー Windows プログラムを開発支援する C 言語自動生成プログラムです。 ・ Windows 上でメニュー、ダイアログボックス等のプロトタイプの設計・リ ・ Microsoft WORD for Windows の WordBAS ℃と WindowsDLL を活用した WORD のアドオンツールです。 ンクが、マウスで設定することができます。 ・ヘルプシステム作成においてのヘルプトピックの作成、ハイバーテキスト ・動作確認用のアニメーションテストモードを装備しています。 ジャンプやルックアップポックス用ジャンプの作成などが簡単にできます ・高機能なダイアログボックスエデイターを内蔵しています。 ・ヘルプシステムを作成するにあたり必要なファイル ( RTF , H 円 , HH ) が全て ・各 Windows メッセージ及び DDE メッセージのサポートをしています。 自動生成されます。 ・ MDI アプリケーション作成のサポートもしています。 ・ BMP データの挿入も簡単にできます。 ・設計された内容は、 Windows アプリケーション作成に必要なファイルとし ・ WORD の既存文書をもとにヘルプファイル作成もできます。 て全て ANS ト C のコードとして自動生成されますが、下記の Switch モジュ 動作環境として ール ( 別売 ) を組み合わせることによって各種プラットフォームにも対応 1. Microsoft Word fo 「 Windows Ver1.2 します。 or 1 .2A MFC C 十十モジュール : 2. Microsoft Windows 上で利用可能なへ \ 75.000 ルプコンバイラ (HC. EXE etc) が必 OWL C 十十モジュール : 要です。 \ 75.000 ( 発売未定 ) 動作環境として く資料請求番号 009 上 > Microsoft C5.1 0r6.0 十 SDK Microsoft C/C 十十 V7. OA 闃工クセルソフト株式会社 Borland C 十十 Ver2.0 以上な せ 〒 108 東京都港区三田 2 ー 10 ー 5 工ル・アルカサル三田 403 どが必要です。 先 TEL ( 03 ) 5440 ー 7875 FAX ( 03 ) 5440 ー 7876 6 第。 N98 を有限会社原田システム設計 標準価格 : →プロ版フルセット版業務用 240 関数 Y99 ′ 800 先〒 320 宇都宮市弥生 1 ー 1 ト 8 インターミディエイト版ーフルセット版個人用 240 関数 Y24 ′ 800 対応機種 . NEC PC ー 9801 シリーズ TEL 0286 ー 34 ー 5605 対応コン / ヾイラ : MSC Ver 6.0 Quick-C MSC Ver 7 . 0 対応予定 文字列、整備型、浮動小数点型と、タイプ別 フィックス・画面制御 ( テキストの保存等 ) ・ にそれぞれ用意されています。またこれらの 漢字処理・メモリー操作・マウス・サーチ・ 関数には割込み機能をもっており、入力中に 文字列処理・システム (MS-DOS 、マシン情 ファンクションキーやカーソルキーが押され 報等 ) 操作・時間計算・電卓・時計など 240 種 た場合、入力処理を中断し割込みの情報を返 を越える関数群 / します。優れたマンマシーンインターフェイ ■ハードウェアの機能を活かす スを要求されるソフトウェアに威力を発揮し BAS ℃等に当たり前のように整備されて いる命令も残念ながら標準関数ではサポート ます。 ・パワーアップキット / ( オプション ) されていません。ハードウェアの知識がなく 拡張グラフィック、マウス操作、 RC-232C ては作成できないハード依存の機能も簡単に 制御、プリンター制御、メニュー機能等、 C 操作できるよう設計されています。 ー FUNC 刊 0N98 の厳しいユーザーの皆様から ・高水準入力関数 寄せられた要望をまとめ、更に 100 種の関数を アプリケーションを開発する際にはデータ ■豊富な関数群 追加しました。また、パワーアップキットに の入力方法が大きなポイントになります。 C ー FUNC 刊 0N98 はアプリケーション開発 「 C-FUNCTlON98 」には入力時に画面を壊 はオンラインマニュアル ? an . exe が付属して に必要な関数を豊富に用意しました。データ います。オプション倒各 / プロ版 \ 55. 网・個人版 \ 12.000 すことなく入力できる書式指定付の関数が、 変換・ファイル入出力・ RS ー 232C 制御・グラ く資料請求番号 009 下〉 00 第電当互 を忸、、い物に 」えきり l! い : に、輌は - はい : を第、 : 民いコ急え ! 亘い朝第ツ い物、 W 物まい朝い皹」 物物讐、師朝いを用 1 : 去は ・ 3 こ簡に第山い ? 第しを : : い脣聞、信可い ! 住 N ( 。 N98 6- F リ N ( 0N98
C 町℃ー Co 第都 d Ci ー 0 ー要を .0 0 i 「 0 ー . 0 F 0 い 00 Func い 0 宿 Func い 0 宿 A55 日 . H い町 TS . 日 0A1. 日 DA 純 . 日 ST 此 . H い 5 t れ c i 代 nd - 第代ー ・もはや有効ではないアドレス、またはもともと有 効でないアドレスへの longjmp ・浮動小数点のアンダーフローおよびオーパフロー ・ゼロによる除算 マルチモジュールチェック Ultra-C は、以下のようなマルチモジュールチェッ クが可能です。 ・互いに整合しない型で宣言された複数のグローバ ル変数、配列や構造体 ・互いに整合しないサイズで宣言された複数のグ ローバル変数、配列や構造体 ・プロトタイプ宣言がされていない場合でも、関数 呼出しの引数の数とサイズをチェック ( プロトタイプ付きの場合でも ) さまざまな異な る呼出し側順序で呼び出される関数 ■互換性 開発ツールと運用システム生成ツールの 2 つの主な ツールを使用する場合には、この 2 つのツールの間に 互換性が必要です。 Ultra-C は、 ANSI-C と互換性が あるだけでなく、 MicrosoftC や数種類の一般的な市 販のライプラリとの互換性も持っています。また、 DOS 工クステンダの DOS/16M とも互換性を持って c 言語標準仕様 (ANSIN 格 ) います。 れを自動的にコンパイルして実行します。 、または、関数呼出しを入力すると、 Ultra ー C はそ を直接実行できます。コマンドウインドウに式、旦 コマンドウインドウの中では、 C のステートメント ■式評価機能 インドして配布することができます。 Microsoft C で最適化した後、 DOS/16M ローダにバ を開発することができます。このプログラムは、 UItra - C を使用してプロテクトモードのプログラム ます。ューサが DOS / 16M を所有している場合には、 テンダである DOS / 16M と完全に互換性を持ってい UItra-C は、米国 RationaI Systems 社の I)OS 工クス DOS/16M 全く同し機能を開発中にも使用することができます。 ができるので、運用システムの生成に使用するのと す。 Microsoft ライプラリは Ultra ー C に組み込むこと の Microsoft C 最適化コンパイラと互換性がありま Ultra-C は、バージョン 5.1 以降の新しいバージョン Microsoft C できます。 では、任意の ANSI 互換の最適化コンパイラを使用 しています。ソフトウェアの運用パージョンの作成 タイプなどの最も一般的な ANSI 構造をサポート バーです。 Ultra-C は、 struct 代入、 enums 、プロト 制定した ANSI X3J11 委員会の古くからのメン 開発元の米国 Rational Systems 社は、 C 言語標準を ECPU : ー対象 OS : 【動作環境】 80286 以上 (X)S/V 4.0 以上 MS—I)()S 3.1 以上 富士通 FM R シリーズ ( 予定 ) 東芝 J ー 3100 シリーズ ( 予定 ) 各社 DOS / V 仕様機 ( 予定 ) ( ノーマルモードのみ ) ・対応機種 : NEC PC ー 9801 シリーズ い 0 「社朝お ・テータの宣言 4 4 5 第一・ i 0 : 第 ip , 記む”をい 記 d 「を】 982 第を 0 は P00 「ド ) 65 (Ox41) 第 ( 曲ー「 ) : 0 リ叩 0 れ・虻 ) ・ぐ ( 0 対 l) 第 t ⅵ 00 ( 、 bcde つ 5 プラリ関数の strcpy と strcat をテストしています。 以下の例では、配列を宣言し、それを使用してライ プロンプトに対してそのデータを入力します。 データをコマンドウインドウで宣言するには、直接 タを宣言すれば、関数のテストが容易になります。 プログラムをテストできます。関数が使用するデー UItra-C では、プログラムを作成したら即座にその このリストから項目を選択し、エデイタに切り替え てその項目を修正できます。 ■クロスリファレンス機能 クロスリファレンス機能では、プログラム内の各オ プジェクト相互の結びつきを検査し、特定の関数や 変数を参照するメモリファイルの一覧を表示 / 編集 第し 0 ー d 0i8 ー 03 できます。 ( 0 ー 3 な貯謝 ⅱ nd 朝「 c な 第一一 0 defio CIRCLES. ( 取代ー を 0 佃ー礪す日をま ロー C し . C ( uSC 」 [ 0 第 i00 de 5 io. h P 「 0t0 ー Y00 printf Punct 日 0 「朝 3 ーⅱ be け ( ⅲ S こ . 純 ) vOid ー ( ch ー「・一 NSC. に ・デバッグ機能 CO 40d ch 20 ] : 記 d ーに 5 を一 040 : ・れ py ( 町 ”い物 0 ー k & つ : れ「いいも物Ⅱ ke ド ) : 記む一 40 : ー・ 0 「 ks をいれ曲 ) like this!" ■インスペクトコマンド のレベルのデータ項目を表示させることができます。 タ項目の値を表示します。データ項Ⅱを選択して次 そこに複合データ項目のアドレスまたは単一のデー プ内の対象項目に関してウインドウをオープンし、 トコマンドを使用します。このコマンドは、スコー 変数の値をチェック / 変更するのには、インスペク UItra ー C デバッガは、プレークポイントの設定、変数 の参照、データの監視、コードのステップ実行、 バックトレースなどを行うことができます。また、 各種のステップコマンドを提供するだけでなく、 1 回 のキー操作で呼出し側の関数を見ることができる、 視覚的パックトレースも備えています。 index. 貶 ( 0 ) 0500 : 6 純ド : NO ・ 00X を : 0 トーー・ na ー 0. ー猷朝 de 新 、 00 を・ 0 を一い 第一 0 を P00 ー計「 ” He 日 0 物 ( 脈 0 ) 0588 : 6 ー 06 : ” Good ト ye ! ” 0000 : 0000 ( 0 引 l) ■プラウサ 0 ⅱ社日・ 3 ねは学 0 物 ) : p ⅱ 0 日に宿宿一心 ) : 新けにい bbbb 新” ) : 0 ⅱーい 0000000C0 第 0 - ) : 0 ⅱ 0 を・ 0 . つ : し 00 第一ミ ・ 9 ( 0 7 ) -(int) Co 第 nd しい 0 ー 00 ・ 5 を p. 0 プラウザでは、ロードされているプログラムファイ ルの概要をみることができます。大きなプログラム の構造を見ることができるので、メンテナンスが非 常に簡単になります。 1 .6MB 以上 ( 2.0MB 以上が望ましい ) ※ EMS 領域を除く 【お求め方法】 当社製品は、パソコンソフト取り扱い店でお求めいただ けますが、弊社に直接ご注文の際は、 Programmer's INDEX (TEL : 03 一 3293 一 3887 ) までお問い合せください。 ・ Ultra-C ( ウルトラ C) は、株式会社 LIFEBOAT と米国 Rational Systems 社の共同開発製品です。・ Ultra-C は、 く資料請求番号 01 1 > ・メモリ・ ■オプジェクトコードのロード UItra-C では、オプジェクトコードやライプラリを 直接ロードすることが可能です。この機能により、 市販のライプラリを利用したり、アセンプラで記述 されたモジュールをロードして利用したりすること が可能です。 Microsoft C との互換性は、この機能を 利用して、 Microsoft C のライプラリを読み込むこ とにより実現しています。 ・巨大ブログラムもサポート Ultra-C は DOS 工クステンダ DOS / 16M の機能を活 用しており、扱えるプログラム・サイズには事実上 制限がありません。 価格 \ 58 , 000 ( 税別 ) 米国 RationaI Systems 社 Instant-C の日本語版です。 ・ UItra-C 、 Instant-C および DOS / 16M は米国 RationaI Systems 社の商標です・一般に会社名、商品名は各社の商 標または登録商標です。・価格、仕様、その他を r ・告なく 変史する場合があります。 株式会社 LIFEBOAT 〒 10 ー東京都千代田区神田錦町 3 ー 6 PHONE : 03-3 四 3-4711 代 FAX : 03-3 四 3-4710
Li st 1 List 1 209 : enum 移動方向 210 : { 右 , 211 : 静止 , 212 : 左 , 213 : 214. 215 : 216 : / * ユーザー操作の自機 * / 217 : EXT Sprite 自機 , 218 : EXT Sprite 蓄積 sp; 219 : EXT Sprite やられ自機 ; 220 : 221 : / * 敵キャラクター * / 222 : EXT Sprite MCMAX_NUM] : 223 : EXT Sprite タイプ A ; 224 : EXT Sprite タイプ B : 225 : 226 : / * 画面に存在する弾 * / 227 : #define MAX_BOM 12 228 : EXT Sprite N[MAX_BOM] ; 229 : EXT Sprite 弾ダミ 230 : 231 : / * 爆発パターン * / 232 : EXT Sprite 爆発 0 ダミ 233 : EXT Sprite 爆発 [ 期 X ー NU 町 : 234 : 235 : / * 現在の敵キャラ数 * / 236 : EXT int 敵数 ; 237 : 238 : / * 現在の弾キャラ数 * / 239 : EXT int 弾数 ; 240 : 241 : / * 現在の爆発数 * / 242 : EXT int 爆発数 ; 243 : 244 : / * 経過時間カウンタり 245 : EXT int 経過時間 : 246 : EXT int ランク 247 : #ifdef MAIN 248 : = 30 : 249 : #else 250 : 251 : #endif 252 : 253 : / * 自機が弾を発射する為のフラグ * / 254 : EXT int 弾発射 : 255 : 256 : / * スコアカウンタ * / 257 : EXT int スコア : 258 : EXT int 蓄積 , 259 : EXT int 終 : 260 : / * sprite. c * / 261 : 262 : void display—sprite (Sprite, int) ; 263 : void delete—sprite (Sprite) ; 264 : Sprite dup—sprite (Sprite) ; 265 : Sprite def_sprite (enum sp_type, PCG * * * , int) : 266 : Sprite def_sprite_dummy (enum sp_type, PCG * * * , int) : 267 : void move_sprite diff (Sprite, int, int) : 268 : void move_sprite_abs (Sprite, int, int) : 269 : void select_sprite_pcg (Sprite, int) : 270 : void select_sprite—color (Sprite, int) : 271 : void select_sprite_h_invert (Sprite, int) : 272 : void select_sprite_v_invert (Sprite, int) : 273 : / * 割り込み処理関数 * / 274 : void vsync_disp (void) : 275 : 276 : / * utiles. c * / 277 : volatile void game_abort (char * ) : 278 : void *xmalloc (int); 279 : void ロードデータ (void); 280 : void 画面初期化 (void); 281 : void 画面終了処理 (void); 282 : void init-trap14 (void) ; 283 : void 消去記録 (Sprite, int); 284 : void 消去笑行 (void); 285 : 286 : / * clash. c * / 287 : void 当たり判定 (void); 288 : 289 : / * enemy. c * / 127 : typedef struct { 128 : 129 : short dif_x; 130 : short dif—y; } move; 131 : 132 : typedef un i on { 133 : 134 : *move_array ; int (*move-func ) ( ) ; 135 : ) move—def : 136 : 137 : typedef s truct SP-DATA { 138 : 139 : enum sp_type type : short 1 i fe : 140 : 141 : short registance; 142 : short work0; 143 : short 響 orkl ; short 響 ork2 : 144 : 145 : short 響 ork3 : sprite body[3][3] : 146 : 147 : move_def def_move : 148 : short *an i me_def : ) *Sprite; 149 : 150 : 151 : / * スクロールレジスタと同等の構造体 * / typedef struct { 152 : 153 : short scO_x_reg : 154 : short scO_y_reg ; 155 : short scl_x_reg; 156 : short scl_y_reg; 157 : short sc2_x_reg : 158 : short sc2_y_reg; short sc3_x_reg ; 159 : 160 : short sc3_y_reg : 161 : } CRTC_REG; 162 : 163 : CRTC_REG scroll_data; 164 : 165 : EXT volatile int vsync_counter; 166 : EXT volatile int sp_is_ready; 167 : EXT volatile sprite request_def; 168 : 169 : EXT PCG *current_def_pcg—ptr[4] : 170 : 171 : 172 : typedef s truct 173. short num_p ; 174 : PCG **pcg_ptr—ptr : 175 : 176 : } GAME_PCG; 177 : 178 : EXT struct load_file 179 : { GAME_PCG *pcg; 180 : 181 : Char *file name; 182 : Char *move file_name; 183 : short *moves : 184 : } load-files ロ 185 : #ifdef MAIN 186 : 187 : 188 : 189 : 190 : 191 : 192 : 193 : 194. 195 : #else 196 : 197 : #endif 198 : 199 : enum キャラタイプ 200 C- 自機 , 201 : 202 : C ー蓄積 , 203 : 204 : C ー敵 0 , C ー敵 1 , 205 : C ー爆発 0 , 206 : 207 ・ 208 : ョ 1 111 ョ 0 ーブー′ー 1 ー 卩 ミ 機積船形発 自蓄弾風変爆 第 130 C MAGAZINE 1993 5