プラの力を借りないて℃言語だけて、「ちゃん C のそれとはだいぶ違うという認識なのて、 , 時間といったところて、しようか。いちおう , とくに「ゲームプログラムをやってみたい」 横 18 タイル縦スクロールを実現して , マッ としたラスタースクロールゲーム」が記述て、 と思って X680X0 シリーズを購入された方々 きたのて、す。 プデータもそれにあわせて書き換えを行い が , 「アセンプラて、なくちやダメ」といった おもしろいゲームは , 高度なプログラム 認識を持たれるのが恐いのて、す。 だけて、て、きるのて、はなく , むしろアイデア ラスタースクロールーてみ と綿密に設計されたグラフィックやスプラ このチャレンジはそこそこの成果を納め よう たようて、 , 初代 X68000 10MHz て、もこのサ イトのデータ構造や管理方法のほうが重要 最近は , このゲームプログラムの作成に ンプルゲームはちゃんと動きます。アセン て、す。最初は , X-BASIC て、作って BtoC て℃ も X68030 を使うようになっていたのて、 , コ ラスタースクロールと半透明を実現する処理 ( map. c) ンパイル時間は従来の 1 / 3 程度に短縮されて います。もし , X68000 10MHz て、処理が間 に合わなかったら「 X68030 専用て、す」て、逃げ てやろうと思っていました。こんなことを 思うからバチが当たったのて、しよう。そう 簡単には思うように動いてはくれませんて、 本連載て、 , 何度かラスタースクロールを 扱いましたが , やり方は目を覆いたくなる ようないい加減な方法て、した。画面上の Y 座 標に対応するラスター位置を正確に把握し てプログラムしたわけて、はなかったのて、 , そのようなデータを得るために多大な時間 を消費してしまいました。 自分て、は , スクロールさせた量とマップ データの位置て、ラスタースクロール範囲を 計算させたツモリなのに , 何度やってもち ゃんと指定した範囲に納まってくれないの て、す。ただ単に「いい加減」な値て、計算した のて、はうまく動くはずがないってことだっ たのて、すが・・・ もうひとつ , ラスタースクロールに ' わったわけがあります。それは , 「ラスター スクロールのような重い処理は , C のような 。級て、は無理。アセンプラて、なくっち [ÄJ に一口ロ やダメ」といった風潮を打破したいがためて、 もあります。 一般的に , アセンプラのほうが記述やと くにデバッグが難しいために , 速度を要求 されるゲームプログラムて、はアセンプラブ ログラミングが一種のカリスマ的な次元ま て、あがめられる傾向があります。私は , 確 かにアセンプラの処理速度は認めますが , アセンプラブログラミングの敷居の高さは 、 0 List l:#include "game. h ” 2 : 3 : / * #define MAP—IS—FILE * / 4 : 5:#ifndef MAP_IS_FILE 6:#include ”図 ata. h ” 7:#endif 8 : 9:#incIude ” sindata. h ” 10 : ll:static int 面初期化 : 12 : 13 : Vram 書き込みアドレスり 14:static unsigned char *vramO = (unsigned char の ( 0XC80000 - 0X400 * 272 ) ; 15:static unsigned char * v 「 a 田 1 = (unsigned char の ( 0Xd00000 ー 0X400 * 272 ) : 16 : 17:static int now_counter; 18 : s tat i c uns i char *map data—ptr0 : 19:static unsigned char *map—data—ptrl; 20 : 21:#define RASTER—MIN 40 22:#define RASTER—MAX RASTER—MIN + 512 23:#define RASTER_STEP 32 24 : 25:static int *raster—map; 26:static int scroll_counter; 27:static short do_raster; 28:static short start_raster = RASTER MIN; 29:static short end_raster = RASTER_MIN; 30:static int index_val : 31 :static CRTC_REG raster_val; 32 : 33 : / ネ 16 * 16 矩形を指定された Vram アドレスに書き込み * / 34 : 35:#ifdef SOPT_TRANS 36:static void 37 : wr i te_vram (uns igned short *vr, uns short *dat) 38 : { 39 : int i; fo 「 (i = 0 : i く 16 : i + + ) 40 : 41 : 42 : 43 : 44 : 45 : 46 : 48 : 49 : 50 : 51 : 52 : 53 : 54 : 55 : 56 : 57 : 58 : 59 : 60 : 62:#eIse 63:#define DMA_SENS 64 : ( { register int d0 asm ("d0") ; 65 : d0 = 0x8d; asm volatile ( ” trap # 15 ”・” = d ” ( d の : ” 0 ” ( d の ) : 66 : unsigned short *P = vr; 事 p + 十 : *dat 十十 : * p + + ニ *dat 十十 : * p + 十 = *dat + 十 : 事 p 十十 = *dat 十十 : * p 十十 : *dat 十十 ; * p + 十 : *dat 十十 ; * p + 十 = *dat 十十 : vr + = 0X200 : 0 120 C MAGAZINE 1 的 3 9
ムプロクラミンク大作戦 sizeof(aniPat)/sizeof( * aniPat), patBa seName, . " ) ; を追加します。デストラク タ Enemy( ) : : Enemy( ) ; にも List 13 の 表記を忘れず追加します。 あとは画面に描くときに , 適当にパター ンを切り換えればよいわけて、す。メンバ関 数 draw ( ) を奪ってしまいましよう。 count と tic て、タイミングをいいかげんに決めてい ます。 aniPat に加えて Creature クラスから 引き継いて、いた Pat もムダなく利用しましょ サンプルは足がヘコへコする程度て、すが , もっとうにやにやと動かしてもおもしろい て、しようね。爆発と違ってあまり変化の大 きい動きをさせるよりは , 小さい動きのほ うがうねうねして火星人らしいかと思いま す (List 14 ) 。 火星人も 1 匹て、はつまらないのて、軍団を出 したいと誰もが思うて、しよう。が , いまの ざ - なる展開のために i / = 1 の { ー 1 ; 使えるのて、 , 直線的なプログラムの展開が インタのリストか配列を使えば仮想関数を クラスから派生させて Enemy クラスへのポ いろんなタイプの敵を出すときは Enemy 98 & 00S / ゲー 望めるはずて、す。 今インスタンス enemy を使用していると ころを , 配列やリストをループて、回すよう にしてやればそのままて、きるはずて、す。 サイルをたくさん打ちたいときも同じよう に , 各ミサイルのインスタンスを配列なり リストなりて、まわしてやれば実現て、きると 思います。 と伝達するだけのことなのて、すから。 して順番になにしろ , あれしろ , これしろ 要するに , 存在する Creature すべてに対 問題点 送に書き換えれば相当改善されるはずて、す。 こをバイト単位の転 っているためて、す。 ern : : draw( ) ; がまじめに 1 ドットずっ打 の速さて、す。遅いて、す。これはもう , Patt 星人の歩く速さて、はなく , ゲームそのもの いちばん目立つのが速さの間題て、す。火 たくさん ( といってもミサイルと爆弾と火 星人と自機 ) が同時に動くとガクっと遅くな ります。こが速くなれば複数の火星人を 出してもゲームとして成立するて、しよう。 うちの PC ー 9801 は UX なのて、 , ちょっと遅 すぎて使いものにならない感じて、す。また マシンの速度によってゲーム全体の動きが 変わってしまいます。速いマシンと遅いマ シンの差をなくすことが必要な場合は , vs ync ( 垂直同期 ) 割り込みなりタイマ割り込み なりを使って制御をしなくてはなりません。 次がキーセンスの間題て、す。いまのまま だと動かしながらミサイルを打っことがて、 きません。ミサイルを打っと自機が止まっ てしまいます。 サ、プルプログラムについて 国キーて、左右 , ーキーて、ミサ イル発射て、す。ゲームオーバはありません。 自機のストックは無限て、す。やられてもや られても続けられます。やめたいときは亘 キーを押せば終了します。 また , 基本的に AT 互換機て、作成してある のて、 , PC ー 9801 版て、はパレットの初期化が行 われません。デフォルト ( アナログモードて、 ままて、はいろいろ不都合があって ( 具体的に は遅すぎて ) うまくいきません。て、すから , is 爆発が火星人たった場合 とりあえず指標だけて、い・ ava i 1 ニ FALSE; Creature : :bomb() : void Enemy: :bomb() { for ist 自分・火星人のアニメーションの下地 0 ; i く sizeof(aniPat)/sizeof(*aniPat) ; (int i delete aniPat[i]; ist 得点の表示 lSt 火星人のアニメーション void drawScore() { static int ScoreX = 200 ; static int ScoreY ニ 360 ; static int S paceing = 24 ; int s = SCORE; p + = Point(Spaceing, digitCs/i]- 〉 draw(p) ; = 10000 : i 〉ー for (int i POint p(ScoreX, ScoreY) ; void Enemy::draw() { static count; static Size static tic count 十十 : sizeof(aniPat)/sizeof(*aniPat) + 1 ; の ; count % = Size * tic,• if (count / tic ー Pat- 〉 draw(origin) ; else aniPatC(count/tic)-1]->draw(origin) : 98 & DOS / V ゲームプログラミング大作戦 77
新発売を 仮想メモリライプラリ マジ、一ル 定価 9 , 800 円 ( 消費税別 ) X 68 k 活用講座 「マジックファイル」は小さなメモリで大き なファイルなどを効率良くアクセスするた めの、 C 言語プラグラミング支援ライプ ラリです。 1 .8086CPU で Long 長の仮想メモ功く複数個 扱えます 2 . 仮想メモリへのアクセスはすべて関数を 通じてアクセスします。メモリプロックのロック 等を意識する必要がありません。 3. 仮想メモリ内のプロンク転送が瞬時に行え ます。 4 . 主記憶、 EMS 、 XMS 、外部記憶を効率よく 統合化できます。 本ソフトにはサンプルプログラムとして工 デイタ (PicoSoft EDITOR) を添付し ています。 「マジックファイル」を使用して作成した 工デイタ本体とエデイタのソースコードが 含まれています。 ( ライプラリソースコードは 含まれておりません ) List 1 68 : )) 69 : 70:#define DO-DMA(BUP, DAT) 71 : do { register int d0 asn ( ” d0 つ : register int dl asm ( ” dl " ) ; register int d2 asm ( ” d2 " ) ; 73 : register VOid 事 al ("al"); reg i S ter VO id *a2 asm ( " a2 ” ) ; d0 = 0x8b : dl = 0b1 ー 000 ー 01 ー 01 ; 77 : d2 = 16 : 78 : al = (BUF) : a2 = (DAT) ; asm ( 80 : ” trap # 15 ”・ 81 : 82 : 83 : } while ( の : 84 : 85:typedef struct 86 : ( 87 : unsigned char *addr; 88 : unsigned short count; 89 : ) array—chain : 90 : 91:static void 92 : ” i te_vran (uns igned char *vr. uns igned char *dat) 93 : { int i; 94 : static array-chain bufC16]; 95 : 96 : while (DMA SENS) 98 : for (i ニ 0 : i く 16 : i + + ) 99 : bufCi]. addr 100 : buf[i]. count = 32 ; 101 : vr + = 0X400 ; 102 : 103 : 104 : DO_DMA (buf, dat); 105 : } 106 : 107:#endif 108 : 109 : void write-one—blockO ( ) 110 : { static int write—count; 111 : 112 : write_vram ( a , (unsigned char の聞 p data[nap-data—ptr0[now—counter]]) ; 113 : vran0 + : 32 : 114 : write—count + + ; i f ( 響ⅵ te—count = = H-BLOCK—NUM) 115 : 116 : wr i te_count = 0 ; 117 : ” a ー = ((H—BLOCK—NUM * 32 ) + 0X400 * 16 ) ; 118 : i f (vramO く (uns igned char の 0XC0000 の 119 : ” a 用 0 = (unsigned char の ( 0XC80000 ー 0X400 * 16 ) : 120 : 121 : 122 : } 123 : 124 : void write—one—blockl ( ) 125 : ( 126 : static int write—count; 127 : write—vram ( a 田 1 , (unsigned char の map—data[map—data—ptrl[now—counter]]) : 128 : vraml + = 32 ; 129 : write_count + + : if (write—count = H—BLOCK-NUM) 130 : 131 : write_count = 0 : 132 : vranl - = ((H—BLOCK-NUM 事 32 ) + 0X400 本 16 ) : 133 : i f (vraml く (uns i gned char の 0XC8000 の 134 : a = (unsigned char 事 ) ( 0Xd00000 - 0X400 事 16 ) : 135 : 136 : 137 : } 138 : 139 : void ロードマップ (int 面 ) 140 : { static char file name ロ = "han. dat ”・ 141 : char na [ 128 ] : 142 : unsigned char gbuf[256] : 143 : 144 : FILE *file; file-nane[3] + = 面 : 145 : strcpy (name, LOAD-DIR) : 146 : strcat (nane, file name) : 147 : file ニ fosm (name, ”「 b " ) : 148 : if (!file) 149 : ganeN)rt ( ”マップファイルがありませんつ : 150 : fread (g-palet, sizeof (g—palet), sizeof ( c 「 ) , file) ; 151 : if (!nap-data[0]) 152 : 153 : 154 : d0 ; つアイを第 ) ー第ス ~ 動作環境 ライプラリ : MS-DOS 汎用。 Microsoft-C 、 Borland ー C とリンクで きます。 ェデイタ : PC ー 9801 / 9821 専用 PC ー 286 / 386 / 486 本ソフトはピコソ列咽が開発し、アンテナ ハウス株が販売する製品です お問合せ、お買求めは イアンテ -jJ コ株。部 〒 102 東京都千代田区ニ番町 1 ト 9 花ビル TEL 03-32 引 -9631 FAX 03-3221-9975 く資料請求番号 121 〉 X68k 活用講座 121 0
特集実践 C テクニカルファイル 画面の任意の場所て、入力を行うには , カ ーソルを入力したい場所へ移動する必要が ある。カーソルを移動するにはふたつの方 法があり , ひとつはコンパイラに付属する ライプラリを利用する。 BC 十十て、は gotox y( ) , MS-C て、は settextposition( ) などが 98 : } 101 : / * 126 : / * 147 : / * 165 : / * : $include く stdio. h> 27 : } ; 58 : / * ある。 もうひとつは , 工スケープシーケンスを List 数値入力関数 24 使用する方法だ。ェスケープシーケンスと 下のエスケープシーケンスを用いる。 がて、きる。カーソルの移動を行うには , 以 てやれば , いろいろな画面制御を行うこと 文字列を printf( ) などて、コンソールに出力し の先頭が、、 0x1b 〃て、始まるものをいう。その は画面を制御するための文字列て、 , 文字列 ESC [pl; pcH List 24 } else カーソルを可行 , pc カラムに移動する ble 6 ( 39 頁 ) に示す ( 工スケープシーケンスは 使用するエスケープシーケンスの一覧を Ta 同様に本例題て、作成する数値入力関数て、 などとすればよいだろう。 / * カーソルを row 行 , c 引 um 桁へ移動 * / printf("*xlb [%d ; %dH", row, colum) ; C 言語て、使用するには , 3 : 4 : 5 : 6 : 12 : 15 : 20 : 21 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 31 : 32 : 33 : 34 : 36 : 39 : 40 : 42 : 43 : 44 : 45 : 47 : 48 : 49 : 50 : 52 : 53 : 54 : 56 : 59 : 60 : 62 : 64 : 65 : 68 : 69 : 72 : 73 : 77 : 78 : 80 : 82 : 83 : 2 : #include $include 7 : #define 8 : #define 9 : #define 10 : #define 1 い $define : $define 14 : $define $define 16 : #define 17 : $define 18 : #define enum n um く conio. h 〉 $include く stdlib. h> く string. h> $include く ctype, れ〉 : maxn(fm->radix][l]; P / * p ニ比較文字列ホ。インタ * / nl = strlen(pp); if ((pp = strtok(buf, FALSE TRUE ESC CR CLS ( ) 0 0xlb 0x0d 0X08 LOCATE(). の CURSOR_ON() CURSOR_OFF() CURSOR_SAV E ( ) CURSOR_LOAD ( ) -rad i x ー / * 工ストアキ - / * リ外ンキー / * ハ・ツクスへ。ヨケー printf("YxlbC2J"); printf("YxlbC%d;%dH ” , 「 . printf( ” YxIb[ 〉引") printf( ” \X1b[〉5ド) printf("hlb[s") printf( ” \X地朝") / * 符号なし 1 0 進 / * 符号なし 1 0 進 / * 符号付き 10 進 / * 符号付き 10 進 1 6 進 ( int) / * 1 6 進 ( long) c) 厄 n2 : strlen(p); if ( nl く n2 ) return TRUE; if (lenl 〉厄 n2 ) return FALSE; return strcmp(p, return TRUE ; 100 : statiC int chk_l 、 ange(int sign 、 long *n, NUMFORH *fm) / * 画面クリア * / / * 0 トト / * カーソルわ * / / * カーソルオフ / * カ - ソル情報セーフ・ * / / * カーソル情報ロード * / 数値の範囲チェック 引数 int sign: NUMFORM * 新 : 戻り値 / * 文字列長で比較 数値入力構造体へのホ。インタ 0 : 負数入力不可 1 : 負数入力可 RADIX_DEC16 RADIX_DEC32. RADlX_SDEC16, RADI X_SDEC32 , RADI X_HEX32 RADlX_HEX16, typedef struct { (unsigned int) (unsigned long) * / (long) (int) TRUE: 範囲内 F 乢 SE : 範囲外 int if (sign) { long int long long int 35 : } NUMFORM," num, rad i X : max num : (*errfunc) (int); / * 数値入力関数の書式指定 / * 初期値 ( i ) , 入力値 ( 0 ) / * 基数 / * 入力可能な最小値 / * 入力可能な最大値 / * 工ラ - 時の巧ー、 0. i f i f i f } else return fm- >naxnum ; if ((unsigned long)fm->minnum く = (unsigned long)*n (unsigned long)fm->maxnum) く : if ((unsigned long)fm->maxnum く (unsigned long)*n) *n : fm¯>maxnum; fm—>minnum; if ((unsigned long)fm->minnum ) (unsigned long)*ll) *n - (fm->minnum く = * 0 & & *n く = fm->maxnum) FALSE; return TRUE; (unsigned long)*n (fm->maxnum く *n) (fm—>minnum > *n) return TRUE; 37 : static int keyin(int ro 町 int COI) キー人力 引数 int 戻り値 int int C : 125 : static void sftl-buf(char *buf. int c, int sign) COI; 入力文字 * 文字配列左シフト * 引数 char int int なし * 戻り値 *buf ; sign: 文字列配列へのホ。インタ 入力文字 0 : 負数入力不可 1 : 負数入力可 し OCATE ( 「 0 第 co l) : CURSOR_SAVE(); CURSOR_ON ( ) : return CURSOR_LOAD ( ) : c ニ getch(); 人力行 人力桁 / * カ - ソル復帰 * / / * カ - ソルオン / * カ - ソル保存 * / / * 入力位置 * / 57 : static int chk-numstr(char *buf, NUMFORM * 価 ) * 数値の範囲チェック ( 文字列によるチェック ) 85 : 86 : 88 : 89 : 172 : 170 : 169 : 8 : 166 : 163 : 162 : 1 6 い 160 : 新 9 : 燔 8 : 156 : 155 : 新 3 : 152 : 15 い 150 : 149 : 凵 8 : 145 : 144 : } 143 : 142 : 141 : 140 : 9 : 138 : 137 : 136 : 135 : 134 : 133 : 132 : 131 : 130 : 129 : 128 : 127 : 124 : 122 : 120 : 1 1 8 : 115 : 112 : IIO: 109 : 108 : 107 : 106 : 105 : 104 : 103 : 102 : 99 : 95 : 94 : 93 : 92 : 90 : len : len = strlen(buf); int } else strcpy(&buf[l]. &buf[2]) ; if (sign & & buf[l] ! = i f い en > 0 ) buf[len - strcpy(buf, &bufCIJ); / * 左へシフト / * ハ・ツフハいれる * / * 引数 *buf; char NUMFORM * 師 : * 戻り値 146 : static void sftr-buf(char *buf) 文字列配列へのホ。インタ 数値入力構造体へのドイ乃 TRUE: 範囲内 FALSE: 範囲外 int static char *naxnC]C 幻 - Char 厄 n し厄 n2. no; / * 比較文字列を選択 * / return TRUE; if (fm->radix = RADlX-HEX16 Ⅱ / * 16 進の時は確認の必要なし * / - 幻 47483648 ” . ” 2147483647 ” } -32768 ” ” 32767 " } , ” 4294967295 " ” 65535 " } , fm->radix 文字配列右シフト 引数 * 加信 Char 戻り値 なし int len ニ strlen(buf); fo 「 (i len 文字列配列へのホ。インタ buf[i] : buf[i-l]; if (buf[len-l] : buf[0] : ・Ⅱ buf[len-l] ニ , ' ) buf[Ien-IJ : = RADI X_HEX32) 164 : statiC void disp-buf(int 間第 int 00L char *buf, int radix) if (fm->radix = RADIX-SDEC16 Ⅱ fn->radix = RADIX-SDEC32) { - つ ! : NU しし ? 0 . : strchr(buf. no P : maxn[fm->radix)[no]; 文字列配列の表示 引数 int Char int 戻り値 CO に *buf; radix; 表示行 表示桁 文字列配列のネ。インタ 基数 : 10 進数 間 : 1 6 進数 特集 実践 C テクニカルファイル VOI. 2 37
, プロクラミンク道場 Dr. 望洋の コンソール画面制御ライプラリ ( 第 2 版 ) く公開へッダ > 凵 st 【演習 1 】 こに示した以外にも , 画面消 去などのライプラリを作成せよ ・とめ コンソール画面制御ライプラリは , いか がだったろうか。小規模なライプラリて、あ るにもかかわらず , いろいろなテクニック が要求されることがわかっただろう。とく に , 複文に展開するマクロや static を伴わず にヘッダ中に展開された関数は , 利用の仕 方によっては , 工ラーが発生しない ある潜在的なバグて、あった。 【重要】コンバイルエラーが出力されない からといって , プログラムが正しいと思う なかれ を肝に銘じていただきたい 2 : / * コンソール画面制御ライプラリ ( 第 2 版 ) h ” console. 4 : 5 : # i ncl ude く stdio. h> 6 : 7 : #define putxy( x,y,s) ( locate(), y), printf("Xs", s) ) 8 : #define putxyc(), y, c, s) ( locate(), y), color(c), printf("Xs" 9 : 10 : v 0 i d COI 0 r ( i n t c) : 11 : VOid (putxy)(int x, int y, const char *s); 12 : VOid (putxyc)(int x, int y, int c, const char *s); 14 : / * ーーカーソルを ( x , y ) に位置づける一一 * / 15 : static VOid locate(int x, int y) printf("%xlB[%d;XdH" 17 : コンソール画面制御ライプラリ ( 第 2 版 ) く実現部 > LiSt コンソール画面制御ライプラリ ( 第 2 版 ) console. C' void color(int c); VOid putxy( int x, int y, 5 : const Char VOid putxyc(int x, int y, int c, cosnt Char 8 : 9 : # i nc lude 10 : # i nc lude 12 : / * ーー表示色を設定する一一 * / 13 : VO i d COI or ( i n t c ) static char printf("hlB["); putchar()c く 1 の ? ' 3 ' putchar(cindx[c % 10 ] ) : putchar('m') : 20 : 22 : 23 : #undef putxy 24 : #undef putxyc 25 : 26 : / * ーー ( x , y ) の位置を先頭として文字列 s を表示ーー * / 27 : VOid putxy(int x, int Y, const char *s) locate(), Y); 29 : printf(s); 30 : 31 32 : 33 : / * ー - ( x , y ) の位置を先頭として色 c で文字列 s を表示ー - * / 34 : VOid putxyc(int x, int y, int c, const char *s) c 引 0 「 ( c ) : 36 : locate(). Y); 37 : printf(s); 38 : 39 : } 問募集 く stdio. h> ” console. h 本連載て、は , 読者からの質間にお答えし , また希望に応じてプログラムの添削も行う。 下記必要事項を明記のうえ , フロッピーデ イスクにて応募していただきたい ( 短い質間 て、あれば , 書面のみて、可 ) 。なお , 応募いた だいたフロッヒ。ーディスクは返却て、きない ことや , 個人的な質間には一切お答えて、き ことをあらかじめご了承されたい どんな小さな疑問点てもけっこうなのて、 , 気後れせずに , 応募していただきたい ( ただ し , 特殊な題材よりも , 一般性の高い題材 を優先的に取り上げることを , あらかじめ ご了承願いたい ) 。 ( 1 ) 氏名・住所・電話番号 ②匿名希望の有無 / ペンネーム ( 3 ) 質問・相談事項 ( なるべく具体的に ) 宛先 〒 108 東京都中央区日本橋浜町 3 ー 42 ー 3 ソフトノヾンクスクウェア ソフトバンク ( 株 ) 出版事業部 C マガジン編集部 「 Dr. 望洋のプログラミング道場」係 c i ndx ロ ” 0415263777 ”・ [ 3 ] 柴田望洋 , 「ライプラリ開発技法」 , ℃ マガジン』 , V01. 3 , No. 11 , 1991 考文献 [ 1 ] 平林稚英 , 「 ANSI C 言語辞典』 , 技術評 論社 , 1989 [ 2 ] 柴田望洋 , ℃プログラマのための C 十十 しばたばうよう ( 工学博士・九州大学工学部化学機械工学科 ) 入門』 , ソフトバンク , 1991 D 「 . 望洋のプログラミング道場 117
重要詳細の表現も定義も共通性を欠いてい ますから , 文字列を共通的な表現として , 二重派生させたクラスの中て、無理やり画一 的な変換をしてしまう , といったやり方が せいぜいて、す。本稿のコードは , Toy2 と C eramic2 用の実装例を示しています。重要詳 細の文字列表現が , static な配列 outputBuf fer の中に作られます。 記憶クラス static により , この変数はクラ ス〔の全オプジェクト〕のための唯一のイ ンスタンスになりますから , 各オプジェク トがその重要詳細をリクエストされたとき には , 確実にこの共有バッフアが使われま す。 static 変数は , 〔 AT&T の〕 C 十十の Ve r. 2 以降の要請に従って , . CPP ファイルの中 て、明示的に定義し初期化しています [ 4 ] 。 クラス Junk のもうひとつの重要なメソッ ドについて , 説明が必要て、しよう。それは , デストラクタ•Junk( ) て、す。 Junk の唯一の インスタンス変数が , 抽象クラス JunkLink を指すポインタて、あることを , 思い出して ください。て、すから , 破壊すべき大したも のはありませんし , 一方 , JunkLink から派 生するオプジェクトにはインスタンス変数 がありません。そこて、本当に破壊が必要な のは , 二重派生したオプジェクトのもう一 方の祖先の中て、す。 しかし , デストラクタを明示的に呼び出 すことが禁じられているのに [ 4 ] , どうやっ てこのクラスのデストラクタを呼び出せる て、しよう ? そのトリックは , JunkLink に 仮想デストラクタ JunkLink を設けることて、 す。ランタイムに , (Junk による ) これの呼 び出しは , 適切な二重派生クラスのデスト ラクタヘリゾルプされます。 List 1 の , Toy2 の例を見てください。 T 0Y2 : : Toy2 ( ) { } となっています。これ は , ほとんど無駄のように見えます。しか し , 思い出してください , 派生クラスのデ ストラクタは , 最終的に祖先クラスのデス トラクタを呼び出すから , この場合にも本 物の仕事が行なわれるのて、す。 「しかし , オーバヘッドはどうなんだ ? 」 , というつぶやき声が聞こえます。代価を支 払わなければならないことは事実て、す。そ れは , 柔軟性対実行性能のトレードオフと いう , 古くからある間題て、す [ 6 ] 。しかし , オーバヘッドを抑えられる箇所はすべて抑 えてありますし , 非公式なべンチマークテ ストによれば , 性能劣化は予期したほどて、 はありませんて、した。残る疑問は , その他 のクラスをこの混成リストに今後加えてい くには , どうするか , て、す。 混成的なコレクションに加えることのて、 きるクラスに対する , 唯一の実質的な制約 は , それらのクラスが自分自身の説明を提 供て、きる , という点て、す。もしその能力が なければ , そういうオプジェクトは , あな たの顧客の店内の棚に , とうてい陳列して もらえないて、しよう。加えるべきクラスが あるとすると ( それはここて、は Timepiece と しましよう ) , 以下のステップを踏みます。 ①列挙型 classType に , TIMEPIECE という 値を加えます。 ② Junk クラスに以下の三つのメソッドを加 えます。 Timepiece からのコンストラクタ (Junk (Timepiece) ) , Timepiece からの 代入演算子 (Junk& operator = (Timepi ece) ) , そして Junk から Timepiece への変 換演算子 ( 叩 erator Timepiece ( ) ) 。 ③ Toy2 や Ceramic2 に倣って , クラス Time piece2 を作ります。それもやはり , Time piece と JunkLink から二重継承し , Junk の friend<' す。そのインスタンスと outpu tBuffer とそのメソッドのすべてが , priv ate て、す。 以上て、は , 元のクラス Timepiece には , 手 を加えていません。またもう一方の祖先 Ju nkLink にも , 変更を加えていません。 この 3 ステップの過程が , もしかして面倒 に思えるかもしれません。て、もやってみる と , 全然面倒て、はありませんよ。ほとんど の作業が , ェデイタのカット & ペーストや 探索・置換て、済んて、しまいますにれも再利 用の一種て、すね ) 。本格的な作業が必要な唯 ーの箇所は , 重要詳細を与えたり取得した りするメソッドの部分て、す。て、も , それも せいぜい 1 ~ 2 時間の作業て、しよう。 その結果得られる , 扱いやすいカメレオ ン的クラスは , 十分に 1 ~ 2 時間の作業の価 値があります。 OOP の目標のひとつが情報 隠蔽の強化て、あるとするなら , このテクニ ックはまさにそれを達成します。ある種の きわめて複雑徴妙な操作が , 本稿の初めの ほうに書いたような , ごく単純な文のシン タクスの背後に隠されます。そして , OOP のもうひとつの目標が再利用て、あるとする なら , このテクニックは , それを達成する ための新たな手段を提供します。私たちの 努力により , あなたの元のクラスの集合を , あなたの顧客の店頭て、ジャンクとして 失礼 , アンティークとして一一リサイクル することに成功したのて、す。 [ 参考文献 ] [ 1 ] Williams, K. "Tables within tables, COMPUTER LANGUAGE, Aug. 198 9 , pp. 36 ー 43. [ 2 ] EIIis, . A. , and B. Stroustrup. The Annotated C 十十 Reference Manual. Addison-WesIey, 1990. [ 3 ] Stroustrup, B. The C 十十 Programm ing Language. Addison-Wesley, 1986. [ 4 ] Stroustrup, B. The C 十十 Programm ing Language, 2nd Ed. Addison-Wesle y, 1991. [ 5 ] Coplien, J. O. Advanced C 十十 Progr amming Styles and ldioms. Addison-W esley, 1992 , p. 310. [ 6 ] Stroustrup, B. and D. Lenkov. 、 'Runti me type identification", C 十十 Repor t. March-April 1992. J0hn Crawford 氏は , ニュージーランドのオ ークランドエ科大学のコンピュータ研究学 部の正講師。彼のネットワークアドレスは jcrawfor@cs386.ait.ac.n2 です。 カメレオンのように変幻自在のクラス 19
List 1 言語に変換して高速化するのもいいて、しよ う。て、すが , 泥縄的になりがちなインタブ リタ言語から ( X ー BASIC は泥縄になりにく い構造はしています ) きちんとデータ構造か ら設計を行うプログラミングに慣れていか ないと , 絶対に壁にぶちあたります。 X680 00 て、は , C 言語て、「ラスタースクロール」程度 のことはて、きることはわかりました。「遅 い , 遅い」と昨今いわれ続ける X68000 シリー ズて、すが , アクション系ゲームを個人て、作 成するにはまだまだ十分なパワーを持った マシンて、す。ぜひ , チャレンジしてみてく ださい 実際のコーティング List 1 が実際にサンプルゲームて、使ってい るラスタースクロールと半透明を実現する 処理を記述した map. c て、す。仕事のあとの貴 重な睡眠時間 2 日分を消費してくれた「苦労 の結品」て、す ( そこまて、いうかあ ? ? ) 。 指定ラスター位置て、の割り込みは , ゲー ムが始まった直後から常時行われるように なっています。この割り込みの登録は utile s. c の画面関係初期化の関数て、行われます。 初期化時点て、マップデータを解析して , グ ラフィックの横ライン数の何本目を描画し た位置からラスタースクロールを開始して , 何本目て、ラスタースクロールを終了するか をあらかじめ計算しておきます。 さんざん行ったテストの結果 , 256X256 高解像度 31KHz 水平周波数て、は , おおむね 40 ラスターが表示開始位置 , そこから 512 ラ スターて、表示終了というくらいて、プログラ ムを行えばうまく動くようて、す。市販品の ゲームのように何種類かの画面モードに対 応するためには , 各モードて、のラスター開 始位置等を確認して ( 本当は CRTC に設定す る値から計算て、きるハズて、す ) モードに応じ た処理を行うのが適切て、しよう。 マップデータを解析した結果から , 実際 のスクロール量を計測してラスタースクロ ール開始位置が画面に表示されたら「ラスタ fo 「 (i : 0 : i く 256 : i + + ) map-data[i] = xmalloc (sizeof (short) * 256 ) : 155 : 156 : 157 : 158 : 159 : int I,J; 160 : unsigned short *p; 161 : uns i char *q , for (i : 0 : i く 256 : i + + ) 162 : 163 : 164 : fread (gbuf, sizeof (gbuf), s i zeof (char) , file); 165 : P = map-data[i] ; 166 : q = gbuf; for (j = 0 : j く 256 : j + + ) 167 : 168 : 169 : 170 : fclose (file) : 171 : 172:#ifdef MAP_IS_FILE strcpy (name, LOAD_DIR) : 173 : fiIe_name[5] = 174 : s trcat (name, f i le name) ; 175 : fiIe—nane[5] : 176 : file = fopen (name, ” rb ” ) : 177 : if (!file) 178 : game-abort ( " マップデータファイルがありません” ) : 179 : map-data-ptr0 = x 皿 110C (now-counter = filelength (fileno (file))) ; 180 : fread (map—data—ptrO, now_counter, sizeof (char), file) : 181 : fclose (file) : 182 : 183 : # else map—data-ptr0 = MAP—DATA—PTRO; 184 : 185 : map—data—ptrl = MAP—DATA—PTRI; if (sizeof (MAP-DATA-PTR のト sizeof (MAP-DATAMRI)) 186 : game-abort ( " マップデータ異常 " ) : 187 : now—counter = s i zeof (MAP—DATA 円 R の : 188 : 189 : 190 : int i, J; unsigned char buf[H—BLOCK_NUM] : 191 : 192 : uns igned char *P : 193 : p ニ map—data—ptrO : for (j = 0 : j く sizeof (MAP—DATA-PTR の / H_BLOCK-NUM; j 十 + ) 194 : 195 : for (i ニ 0 : i く H—BLOCK_NUM; i + + ) 196 : 197 : fo 「 (i = 0 : i く H_BLOCK_NUM; i + + ) 198 : 199 : p + = H_BLOCK_NUM; 200 : 201 : p - map—data—ptrl : 202 : for (j = 0 ; j く sizeof (MAP—DATA-PTRI)/ H_BLOCK-NUM; j + 十 ) 203 : 204 : for (i = 0 : i く H—BLOCK_NUM; i + + ) 205 : buf[i] = 206 : * p 十十 : for (i = 0 : i く H_BLOCK_NUM; i + + ) 207 : 208 : p + = H_BLOCK_NUM ; 209 : 210 : 211 : 212:#endif 213 : int 214 : 1 ; int 215 : counter ; 216 : raster_map x 110C (sizeof (int) * (now—counter/ H-BLOCK-NUM + 1 ) ) : 217 : 218 : counter for (i = 16 ; i く now_counter / H-BLOCK-NUM; i + + ) 219 : 220 : 221 : 222 : 223 : 224 : 225 : 226 : 227 : 228 : 229 : 面初期化 = 1 : 230 : 231. 232 : 233 : ・ VOid 234. 235:raster-scr011 ( ) 236 : { static short count; 237 : static short last; 238 : static short next = RASTER—MIN; 239 : / * ラスター位置指定レジスタアドレス * / 240 : short *next-inter = (short * ) 0xe8 圓 12 : 241 : 0 uns igned char dat - map-data-ptr0 [s i zeof (MAP-DATA-PTR の counter 十 = 32 : = RASTER_DATA) if (dat ras ter map [ i ー 16 ] = else ras ter—map [ i ー 16 ] - i * H_BLOCK_NUM] : counter : C MAGAZINE 1993 9 122
〇一 〇一 〇一 〇一 26 使って処理することもてきる。 sprintf(dl, "%s%s%s", sl, s2, s3); 単純な連結なら , sprintf ( ) を使うメリッ トは何もないが , 連結する文字列の間に、、 を入れるとか , 幅を揃えるなど , 体裁を整 えた文字列がほしいときは sprintf() のほう t n) 1 : 30 : { ある。 連結する場合について考えてみよう。 strca 次に , 文字列 sl の末尾に文字列 s2 の内容を 文字の幅にし , , 〃て、区切って連結する例て、 sprintf (dl, "%8s, %8s, %8s", sl, s2, ; とすればよい 理するならば , これをあえて sprintf ( ) て、処 t ( ) を使うなら , strcat(sl, (2) ; が便利て、ある。以下は , 、、 sl , s2 , s3 クを最低 8 sp 「 intf ( ) で引数文字列を連結する LiSt 2 : 3 : 4 : 6 : 7 : 9 : 13 : 1 : 3 : 5 : 6 : 8 : 9 : 10 : 16 : 20 : 22 : 23 : 24 : 26 : 27 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 1 : #include く stdio. h> #include く string. h> int main(int argc, char** argv) char dl [ 1024 ] : char* p; strcpy(dl, argv[l)); P = dl + strlen(dl); for (i 2 : i く argc : 十十 i ) p + = sprintf(p, puts(dl) : return 0 : / * p は文字列 dl の末尾を指す * / / * サイズが不足で危険かも知れない * / / * 連結結果を表示する * / sprintf()l 十 strlen(sl), "%s", (2) ・ となる。展開文字列の格納先として文字列 sl の末尾て、ある、、 sl 十 strlen ( sl ) クを指定する のがミソだ。また sprintf ( ) の戻り値は展開 文字列の長さて、あるから , 格納先をその長 さ分だけ進めて文字列末尾を指すようにて、 きる 0List 3 ( 26 頁 ) に示すサンプルプログラ ムて、は格納先 p を sprintf( ) の戻り値て進めて [ 例題 6 ] qso 「 t ( ) へ渡す比較関数 を作る ANSI 規格の標準関数 qsort( ) は , 与えら れた配列をソートする関数て、ある。この関 数の第 4 引数は「関数へのポインタ」て、あり , その型は , int ( *)(const void*, const void* ) qso 猷 ) の使用例 ( これはコンノヾイルエラーになる ) / * ansi6. C ー qsort ( ) の使用例 ( これはコンパイルエラーになる ) * / 2 : # i nc lude く s td i 0. h> $include く stdlib. h> 4 : $defi ne ELEMENTS-0F(array) (sizeof(array)/sizeof( (array) [ 0 ] ) ) qso 猷 ) の使用例 ( 正しい例 ) / * ansi6a. C - qsort ( ) の使用例 ( 正しい例 ) * / 2 : #include く stdio. h> 3 : # i ncl ude く std ⅱ b. h> 4 : #define ELEMENTS_0F(array) (sizeof(array)/sizeof((array) [ 0 ] ) ) int int-cmp(const VOid* a, const VOid* b) void print-int(int* 店 size_ return ヨ : se / * *a く *b * / return 0 : se i f (*a return 1 : while (n--) { i f (n ! = の putchar(' else putchar('Yn'); int main(void) static int test ロ printf( ”ソート前 > " ) ; 4 , 1 , 5. 9 , 2 } : print-int(test, ELEMENTS-0F(test)); qsort(test, ELEMENTS-OF(test), sizeof(test[0]), printf( ”ソート後 > " ) : print-int(test, ELEMENTS-0F(test)); return 0 ; int-cmp) : 5 : 6 : 8 : 9 : 18 : 20 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : int int-cmp(const void* va, const VOid* vb) const int* a = va; const int* b : vb; return 1 ; else if (*a return 0 ; 引 se / * *a く *b * / return ー 1 : VOid print-int(int* p, size-t n) return 0 : print-int(test, ELEMENTS-0F(test)) : printf(" ソート後〉 " ) : qsort(test, E し EMENTS_0F(test), sizeof(test[0]), print-int(test, ELEMENTS-OF(test)) : printf(" ソート前 > ” ) : static int test ロ = { 3 , 1 , 4 , 1 , 5 , 9 , 2 } ; int main(void) putchar('Yn'); putchar( ' i f (n ! : の vhile (n--) { int-cmp) ; 40 : } C MAGAZINE 1993 9
ト 25 開発管理の画一化を強力に推進 する新指向統合プロジェワタ 新機能 管理情報の解析抽出・整形コメント化・ 挿入処理まで C ソース上で自動化実現 新機能 十数種 Df±様を自由フォー乙で印刷で きる出力機 PC201 系・エプソン ESC / P 系プ 新機能 マ リンタ用の新プリントエンジン搭載 幅広い印刷環境を C PA D ガサホート 新機能 多階層インワルードやプリプロ構文に も対応 新機能 新統合環境によりさらに充実の操作性 実践アルコリ解法のテクニック 動的計画によりナッブザック問題を解くプログラム List 6 1 : / * f ⅱ e ニⅱ s t6. c 2 : 3 : * 目的 : 動的計画法により , ナッブザック問題を解く 4 : * 作成 : 松田晋 5 : 6 : コンパイル法 7 : 8 : LSI C ー 86 9 : Borland C + + * 起動法 : 1 1 : 12 : C> ⅱ s t6 データファイル名 * データファイルの形式 14 : 品物の種類数 ナッブザックのサイズ 品物の値段 , サイズ 品物の値段 , サイズ 20 : 21 : #include く stdio. h> 22 : #include く stdlib. h> 23 : 24 : $define MAX-SIZE 25 : #define MAX-ITEMS 26 : 27 : / * 品物を表す型を定義する * / 28 : typedef struct { / * 価格 * / 29 : double price; / * サイズ : 整数型であることに注意 * / 30 : i n t S i ze ; / * 品目番号 * / 31 : int i tem_no; / * 使用量 * / 32 : i nt n : 33 : } item-t; 34 : / * 品物の種類の数 * / 35 : i n t n ー i tem ; / * ナッブザックのサイズ * / 36 : i n t ⅱ m i t , 37 : item-t items[MAX-ITEMS] : / * 品物の一覧表 * / 38 : 39 : / * 動的計画法により , ナッブザック問題を解く ( アルゴリズムの本体 ) 40 : void knapsack(void) 42 : int S, item, space; 43 : double nevvalue; static double gain[MAX-SIZE + 1 ] : 44 : static int ch0iceCMAX-SIZE + 1 ] : 45 : 46 : for ( s = 0 ; s ←ⅱ m i い s + + ) { 47 : gain[s] 48 : choice[s] 49 : 50 : 52 : 53 : 55 : 56 : 58 : 59 : 63 : 65 : 66 : 68 : 69 : 7 4 : / * 入力ファイルからデータを読み込む * / 75 : void get-data(int argc, char *argv[]) 5 / 5 花材 : C 〉 LCC list6 . C : C>BCC list6 . C / * ナッブザックのサイズの最大値 * / / * 品物の種類の最大値 * / 1000 100 CPAD だけのオリジナル機能 / * ナッブザックに詰めた品物の金額 * / / * 最後に詰めた品物 * / / * 以下 , 品目番号 item の品物を考慮に入れていく * / for (item = 0 ; item く n_item; item 十十 ) / * 以下 , 大きさ s のナッブザックに , / * 品物 item を考慮した場合の最適な詰め方を探す * / items[item]. size; S く = limit; s 十十 ) { for (s items[item]. size; space = S - gain[space] 十 i tems[item]. price; nevval ue if (newvalue > gainCs]) { gain[s] nevva ー ue : choice[s] i tem, 0 ; item く n-i tem; item 十 + ) for (i tem items[item]. n ニ 0 : ⅱ m i t ; S vhile (choiceCs] > = の { space ニ choice[s] : i tems Cspace]. n 十十 : items[space]. size; S 標準価格く消費税別〉・・・・・¥ 98 , OOO シリーズ用 ) ¥ 128 , OOO く他機種用〉 ( 教育・企業向けには 10 ユーザーパックもございます ) / * 詰め方を逆順に再現する * / ソフトウェアインフォメーションセンター ( 技術的なこ質問・こ相談にお電話てお答えします。 ) TEL ロ 878-5 ト 1807 受付時間 / AMIO 0 ト解 5 ・ 00 月曜ー金曜 ( 祝日除く ) オオッカ高事システムクリエイト事業部 く商標登録出願中〉 く資料請求番号 091 〉 実践アルゴリズム戦略解法のテクニック 91
の処理は , 字句レベルの処理とは異なり静レベルの get ( ) , unget ( ) 関数を使用して実 と続く可能性も常にあるため , あらかじめ 1 トークンの先読み [ 2 ] を行い解析を終了す 的存続範囲を持っ関数 t 。 ken ( ) に先読みト現することも可能て、ある。 るか , もしくは続行し , 次の数を得るかな ークンを保持するようにしているが , 文字 関数 nexttoken( ) て、は , 次のトークンが , どが先読みにより決定される。サンプルプ のレベルの getc ( ) , ungetc() と同様に , g ・行の終わりて、ある場合 ログラム (List 26 ( 40 頁 ) ) て、は , このレベル ettoken ( ) , ungettoken ( ) というトークン ・数値て、ある場合 switch 文の例 整数配列を整数集合であるかのように扱う List List 28 cd 1 よ cd cd ーよ 8 #include く string. h> static char longer-month ロ = { 1 , 3 , 5 , 7 , 8 , 10 , 12 } : if (memchr(longer-month, Ⅱ , sizeof(longer-month))) List List 29 部分範囲型の数値の集合を定義する例 マクロでくるむ #define ISELEM(elem, set) memchr(set, elem, sizeof(set)) 、 31 = [ 1 , 3 , 5 , 7 , 8 , 10 , 1 幻 ; const longer-month: set Of 1 List 32 List 32 集合演算 2 = nevnode( れ t ) : 60 : 63 : 64 : assert(0) ; 66 : 67 : / * set_member. 68 : * 整数 i が集合 se げの要素かどうかを判定する。 69 : * / 70 : int set_member(intSet *self, int i) intSet *S,t 72 : 73 : for (s=self; s; s=s->next) { 74 : if (s-Yi 75 : return 1 ; 77 : return 0 : 80 : } 81 : 82 : / * set_union: 83 : * 集合 sl と集合 s2 和集合 sl s2 を返す 84 : 85 : intSet *set-union(intSet *sl, intSet *s2) 87 : intSet *result = NU しし: 88 : for ( : sl : sl=sl->next) { 89 : 90 : result ニ set-insert( result,- sl->i ) : 91 : for ( : s2; s2:s2->next) ー 92 : result ま set-insert( result, s2 ー第 ) : 93 : 94 : 95 : return result; 98 : / * set_intersection. 99 : * 集合 sl と集合 s2 共通集合 sl s2 を返す 100 : * / 101 : intSet *set_intersection(intSet *sl, intSet *s2) 102 : { 103 : intSet *result NU しし : 104: 105 : 川 6 : ⅱ (sl->i く s2->i) { 107 : ⅶ i (sl-> i く s2 つ i ) { 108 : sl ニ sl->next; 川 9 : Ⅱ 1 : else if (sl->i s2->i) { ⅶⅱ e (s2-> i く s ト〉 i ) { s2 ま s2->next: else { / * (sl->i Ⅱ 6 : result : set_insert( result, sl->i ) : 引 ニ sl->next; s2 = s2 つ nex t : 0 : old->next return self; い / * intset. c * / 2 : 3 : # include く assert. h> 4 : # i ncl ude くⅱ m i t s 、 h> 5 : #include く stdarg. h> 6 : $include く stdio. h> 7:#include く stdlib.h> 8 : 9 : typedef struct intset { 10 : i nt i : struct intSet *next; 12 : ト intSet; 13 : 14 : # i fndef EXIT_FAILURE 15 : #define EXIT-FAILURE ー 16 : $end i f- 17 : 18 : $define NEW(t) (t*)malloc(sizeof(t)) 19 : 20 : / * newnode ・ 21 : * 値 i をもつ集合の要素を新規生成する。 22 : 23 : static intSet *newnode(int れ intSet *next) 24 : { intset * 「ニ NEW(intSet).• 25 : if (r NUL い { 26 : printf( "Error lex(set_inset 、 t) : heap memory overyn" ) ; 27 : exit(EXIT-FAlLURE); 28 : 29 : 30 : 31 : r—>next = next; 32 return r; 33 : ト 34 : 35 : / * set_insert: 36 : * 集合 se げに要素 i を追加する。 38 : intSet *set_insert(intSet *self, int i) 40 : intSet *s: 41 : intSet *old; 42 : iÄ self->i) { if (self==NULL Ⅱ 43 : return nevnode( i. self ) : 44 . 45 : 46 . for (s=se げ : s : d=s, s=s->nex t) イ 48 : return s 信 / * i は既に se はの要素である * / 49 : 50 : else if (s->i 〉 i Ⅱ s->next==NUL い { 52 : intSet *t : 53 : * i は se げの要素ではないので se げに追加する 54 : 55 : 56 : 57 : 引 d 号 s ; 58 : t ニ old->next; 59 : / * for djgcc's stdlib. h * / 42 C MAGAZINE 1993 9