場合 - みる会図書館


検索対象: 月刊 C MAGAZINE 1992年2月号
112件見つかりました。

1. 月刊 C MAGAZINE 1992年2月号

djgcci* 解講座曲 llo 刪闡 読み込み途中て、データが終わったら EOF を返します。 fread.c int fread (void * ptr, int count, FILE *fp) ; int size, め , 制約文字 ' 十 ' は使用て、きない ( * 2 ) 。 fputc. c します。 ます。実際に読み込めたデータの個数を返 ータを count 個だけ ptr の指す領域に読み込み fp の指すストリームからバイト数 size のデ int fputc(int c, FILE *fp) ・ 文字 c を fp の指すストリームに出力しま す。成功したら c, 失敗したら EOF を返しま す。 マクロ putc (), (p) によって実現されてい putchar. c int putchar(int c) ・ 標準出力ストリームに文字 c を出力しま す。 マクロ putc(), stdout) によって実現され ています。 stdio. h て、は putchar( ) はマクロとして定 義されています。したがって , このヘッダ をインクルードするとこの関数は呼び出さ れません。 fputs. c int fputs (const char * s, FILE * (p) ・ s の指す文字列を fp の指すストリームに出 力します。成功したら s の末尾の文字 ( ・ \ 0 ' の直前の文字 ) , 失敗したら EOF を返しま す。 アセンプラ命令が入出力オペランドを持っ場合 , またはオペランドのビット列の一部分だけが変更さ れるような場合には , オペランドの役割りを論理的 にふたつに分け , 入力オペランドと出力オペランド の両方に指定しなければならない。命令を実行する ときにこのふたつのオペランドが同じ場所になけれ ばならないことは , 制約として指定する。ふたつの オペランドに指定する C の式は同じて、もよいし異な ってもよい ( * 3 ) 。例として , 仮想的な命令。 combine に対して , bar を入力専用オペランド , f00 を入出力 オペランドとして渡すための記述を示す。 asm ("combine % 2 , % 0 ” ( f00 ) " 0 " ( f00 ) , "g" (bar) ) ; 制約。 " 0 " ' はオペランド 1 がオペランド 0 と同じ場所 になければならないことを示す。制約に数字を使用 て、きるのは入力オペランドに対してのみて、あり , そ れらは出力オペランドを参照しなければならない あるオペランドを別のオペランドと同じ場所に置 くことを保証する方法は数字を指定した制約だけて、 ある。 f00 が両方のオペランドの値て、あるという事実 だけからて、は生成コードて、の場所が同一て、あること を保証て、きない。したがって , 次の例は動作しない asm ("combine % 2 , % 0 =r" ( f00 ) "r" ( f00 ) , 'g" (bar) ) ; さまざまな最適化や再ロードによりオペランド 0 と 1 は別のレジスタに置かれる可能性があり , GNU C C はそうしてはいけない理由がわからない。たとえ ば , コンパイラは f00 の値のコピーをあるレジスタに 見つけてオペランド 1 としながら , オペランド 0 を別 のレジスタに出力しておき , あとて、 f00 のアドレスへ コヒ。ーするかもしれない。オペランド 1 のレジスタが アセンプラ命令に書かれていないためこの場合はも ちろん正しく動作しないが , GNU CC にはそれがわ からない 出力オペランドに制約修飾文字、 & ' が指定されてい ない限り , GNUCC は入出力て、ない入力オペランド と同じレジスタを出力オペランドに指定する可能性 がある。すべての入力オペランドは値の出力まてに 使用されると仮定している。この仮定はアセンプラ コードが実際には複数の命令によって構成されてい る場合には成立しなくなる。このような場合 , ' & ' に よって各出力オペランドが入力オペランドと重なり 合わないよう指定しなければならない いくっかの命令は特定のレジスタを破壊する。 れを記述するためには , 入力オペランドの次に三つ / * no outputs * / ("movc3 % 0 , % 1 , % 2 ' として ) 書けばよい。次は vax に実際にある命令の例 目のコロンを置き , 破壊されるレジスタ名を ( 文字列 asm volatile て、ある。 'r0" g" (count) * 2 マシン記述て、は。十 ' は 準み聿きオペランドを示す ル儿日 制約文字て、す。 * 3 たとえば , このすぐあ との例を書き換えて , asm ("combine % 2 , % 0 =r" (quux) " 0 " ( f00 ) , のように , 入力オペランド には f00 , 出力オペランドに は quux という式を指定する こともて、きます。 この場合 , asm 命令の別 の C のコードて、は f00 に書き 込み , asm 命令の後のコード て、は quux を読むことになり ます。 djgcc 詳解講座・ He110 GCC World 79

2. 月刊 C MAGAZINE 1992年2月号

mo t 」に対応しているものて、ある。つまり , これ が int などと同様に変数宣言の基礎となるデ ータ型を表していると考えれば , 取り敢え ずはわかりやすくなるだろう。 さて , そこて、心配になってくるのは , 「て、 は structfoo の具体的な内容はどのように宣 言すればいいのか」て、ある。実は上の変数 n の宣言をファイルの先頭て、いきなり行うと , コンパイラはエラーを出すことになるだろ う。なぜならば ( 心配したとおり ) , struct f 。。の具体的な内容がわからないからて、あ る。 struct の具体的な内容の宣言は , 同様に C の柔軟な宣言の構文によって行われる。た とえば , f00 が double bar と long zot という ふたつのメンバを持つ構造体て、あるとする。 そうすると , この場合には次のように宣言 long struct f00 n ; zot ; double bar ; struct f00 { すればよい struct f00 { すなわち以下のようになる。 融合してひとつにまとめることがてきる。 とを思い出してほしい となると , 両者は というカテゴリに属しているものてあるこ はいずれも文法上は同じ宣言 (declaration) ただいて , C て、は構造体の宣言と変数の宣言 く。前回まての本連載などを参考にしてい , こからバリエーションが展開されてい は必ずセミコロンが置かれる。 はセミコロンが必要てある。宣言の最後に とに注意 ! ) 。複文の場合とは違い い } つの後にセミコロンが置かれているこ る ( なお , structfoo の宣言の閉じプレース 名を用いて実体 ( 変数 ) を宣言するわけてあ 言してそれにタグ名を与え , 次にそのタグ えている ) 。すなわち , まず型そのものを宣 体型の変数の宣言の基本て、ある ( と筆者は考 これカ℃における構造体の宣言とその構造 double long bar ・ zot ; この書き方て、はよくわからないが , 要す るに struct f00 n ; という宣言のタグ名 ( fo 0 ) と変数名 ( n ) の間に具体的な構造体の内部 構造をプレースい、 { 〃と、、ド ) て、囲んて指 定しただけて、ある。以下のように 1 行にする とこの事情が理解て、きるだろう。 struct foo{double bar ;long zot ; } n ・ この宣言によってふたつの動作 , すなわ ち , ①構造体を宣言してそれに f00 というタ グ名を与えること , ② struct f00 の変数とし て n を宣言することが同時に完了する。な お , このように記す場合には閉じプレース の後にセミコロン ( 〃 ) を打たない この書き方て、は変数名 にも注意されたい の後まて、の全体がひとつの宣言て、あり , そ こにセミコロンを打てばよいのだ。また , 一旦このように構造体の実体を宣言してし まった場合には , 以後は structfoo として参 照するだけが許される。再び構造体の具体 的内容を宣言しようとすると , 仮にまった く同じ構造て、宣言を行っても二重定義にな る。 さて , 話をややこしくするのは , struct の 宣言においてタグ名を省略してしまうこと も文法上は可能なことて、ある。 struct { double bar ; zot ; この宣言は完全に文法の規定に沿ったも のて , コンパイラはエラーにはせず , おそ らく警告 (WARNING, お願いだから「ワー ニング」とは発音しないてほしい ) も出さな い。しかし実用上はタグ名を省略て、きるの は typedef と組み合わせたとき ( 後述 ) と考え るべきだろう。なぜならば , この例のよう にタグ名を省略してしまうと , そこて、宣 された構造体を別な場所て、指定する手段が なくなるからてある。たとえば次の例を見 long ていただきたい struct { double long struct { double long bar ・ zot ; bar ; zot ; / * ANSI ではエラーになるはず * / void f00 (void) ANSI C ー more 107 を採用すべきなのかが規格上曖昧てあった。 ascal< は , 同一性に関してどちらのルール 型 ) として見なされることになる。初期の P 順序が同じてあれば同じ型 ( 代入コンパチな つの構造体はメンバの型の構成やその出現 と呼ばれるものがある。その場合にはふた して structure equivalence ( 構造の同一性 ) ある。 name equivalence に対応する概念と 同一性 ) と呼ばれるルールを採用したためて 関して , いわゆる nameequivalence( 名前の これは ,ANSI C が構造体の型の同一性に 用される。 同様のルールは共用体や列挙 ( enum ) にも適 あっても , 無条件に異なる型てある。なお 士は , たとえ中身の構造がまったく同じて、 もちろんタグ名が異なっている構造体同 て、ある。 あれば型が異なるためにエラーになるはず 内部て、の代入は ANSI に忠実なコンパイラて、 されているのて、ある。このため , 関数 f00 の つがユニークな ( 相異なる ) 型てあると規定 体は , 中身の構造とは無関係にひとつひと 型て、あるとみなされる。タグ名のない構造 ている。しかし , ANSI C て、は両者は異なる たく同様の構造体として変数 n と m を宣言し タグ名のない struct を用いて , 実体はまっ

3. 月刊 C MAGAZINE 1992年2月号

ボーランド lnformation from Compiler Makers Turbo Pascal fo 「 Windows ( あるいは Bo 月 and C 十十付属の R esource Workshop) をインストー ルしようとしたら , ダイアログボ ックスの枠だけが表示されて文字 が表示されません。どうしたらよ いのでしようか。 A Windows のインストール時に マルチフォントドライバを指定さ れているのだと思われます。 Turb 0 PascaI for Windows や Resourc e Workshop のインストーラは , シ ステムに組み込まれたフォントの うち最適なものを自動的に選択す るようになっていますが , マルチ フォント ROM*—ドがないときに この指定をすると表示てきないフ オントが選択されてしまいます。 プログラムマネージャのメイン ウインドウからコントロールバネ ルを呼び出してフォントを選んて ください。表示されるフォントの 一覧の中に「標準明朝」あるいは「標 準ゴシック」という名前のフォント がありますが , フォントのサンプ ルには何も表示されないはずて、す。 これらを削除してから , あらため てインストールをやりなおしてく Q PC ー 9801 版の Turbo Pascal f or Windows を , FM-R などほかの 機種でも動作させることができま すか。 A 基本的に , ほかの機種ての動 作確認は行われておりませんのて , 動作の保証はてきません。 実際には , 統合環境はほかの日 158 C MAGAZIN E 1 2 2 本語 Windows< も動作させること がてきるて、しようが , Turbo Deb ugger for Windows は完全に PC- 9801 ( あるいは EPSON 機 ) に依存 していますのて、ほかの機種て、動作 させることはて、きません。 TurboP ascal for Windows の統合環境に はデバッグ機能がありませんのて、 , こうした機種て、はプログラムをデ バッグすることがかなり難しくな るてしよう。 Turbo PascaI for Windows て、 作成したプログラムについては , とくに機種依存させる記述をしな ければほかの日本語 Windows< も 動作可能て、す。ただし , プログラ ミングの際には , 解像度や仮想キ ーの機種間の違いについて配慮し てください Q BorIand C 十十のプロテクトモ ードコンバイラ BCCX や BCX を使っ ているときに , Fig. 1 のようなエラ ーが発生してコンバイルできませ ん。プロテクトメモリは十分にあ るはずですが , BCCX や BCX はそれ を使わないのですか。 A BorIand C 十十て、はプロテク トモードて、コンパイルする場合に も次のような場合には内部的な制 限によりコンパイルてきなくなる ことがあります。 ・非常に大きい switch 文 ・何度にもネストされた if—else 構 文 ・数多くの , あるいは複雑なクラ スの定義 ・多くのヘッダファイル プロテクトモード版コンパイラ (BCCX/BCX)< もコンパイルてき ない場合は , 関数を複数の関数に 分割したり , ソースコードや定義 を分割してコンパイルするように してください Q BCX を起動して F ⅱ引 Get info で拡張メモリの使用状況をみると , fExtended memory in useJ に 0 と 表示されています。 Extended me mo 「 y が使われていないのですか。 A ' こに表示されているのは , BC が DOS のオーバレイ領域として 使うときのメモリの大きさて、す。 BCX は , プロテクトメモリ (Exten dedmemory) をメインメモリとし て使いますのて , Available mem ory の値が増えているはずて、す。 ( 特殊キー ) の代わりが有効になり キーによって十矢印キー 環境のエデイタては十テン たとえば , BorIand C 十十の統合 割り当ては Table 1 のとおりて、す。 して使うことがてきます。おもな カてなくカーソルキーの代わりと ーや十テンキーを数字入 境のいくっかの場面て、は , テンキ ポーランドが提供する統合環 A てしまいました。 ーソルが別のペイン ( 枠 ) に移動し で数字を入力しようとしたら , カ Turbo Debugger でテンキー Q てしまいました。 の組み合わせでカーソルが移動し ているときに , 十テンキー 引の統合環境のエデイタを使用し Borland C 十十や Turbo Pasc Turbo PascaIfor Windows Turbo PascaI BorIand C 十十 Turbo C 十十 TurbO Debugger & T00 ます。 Turbo Debugger て、は , テン キーを十カーソルキーの代 わりに使ってペインを移動するこ とがて、きます。このような場面て , 数字を入力したいときは , フルキ ーの上部にある数字キーを使って ただし , この機能は , 必ずしも すべての場面て、使うことがてきる わけてはありませんのて、 , ご注意 Q BorlandC 十十のライプラリリ ファレンス VoI. 1 で , 関数の右側に 書いてある記号について説明して A C 十十が〇て、囲んて、ある記号 は , その関数が C 十十のモードて、し か使えないか , C 十十専用の関数が ものもあります。 IX の特定のバージョンに依存する SI の仕様から拡張されていたり UN すぎません。関数によっては , AN ている記号は , あくまても目安に こに掲載され します。ただし , 使うことがて、きるということを表 同等の関数があり , Windows< も 仕様には含まれないが UNIX には せん。これは , access は ANSI C の かの項目には網かけがされていま ANSI に網かけがしてあり , そのほ たとえば , access という関数は 様ては使えないことを表します。 号は , 網かけがしてあればその仕 ANSI, UNIX, Windows という記 ての関数を使うことがて、きます。 C 十十のモードて、は , 基本的にすべ 用意されていることを表します。

4. 月刊 C MAGAZINE 1992年2月号

員 mo だが , 配列形式の場合にはそのような場合 にループを構成することて、コンパクトな表 現が可能となる。 一方 , 点ということて、全体をひとまとま りのデータとして捉えて , そのまま代入し たり関数に引数として値を渡したり , 関数 の値として返したりしたくなってくるが , そのような操作は配列て、は不可能て、 , 構造 体て、なければて、きない このため , データ 全体のハンドリングの容易さを考えると構 造体に軍配が上がる。 ときとして , 構造体て、取り敢えずデータ 型を定義しておき , その先頭メンバ ( この例 て、は x) のへのポインタを作り出し , それを 元に配列的にアクセスするというコーディ ングが行われているようだが , これは移植 性を考えるとあまり好ましくない というのも , コンパイラによっては構造 体には穴が存在するかもしれず , 連続的に 宀言したメンバが必ずしも配列と同様にメ モリ上に配置されるという保証はないから て、ある。このため , 構造体のあるメンバの いて三つの座標値を d 。 uble て、表すことを考 アドレスを手がかりにそのほかの要素を配 るというテクニックを使っていることに注 える。この場合単純に考えると配列と構造 列と同じ機構て、アクセスするのは , コンパ 意されたい ( List 3 ) 。 体と両方の選択肢が考えられる。 イラと環境に大幅に依存したコーディング 配歹に構造体の使い分け てある。 まず配列を用いることにすると , 次のよ うに書けるだろう。 それやこれやて、 , このケースて、のひとつ / * 配列の場合 * / の折衷案として , 配列を構造体の中に埋め 最初に述べたように , 配列と構造体は , enum{X axiS,Y axis Z axiS,N axes}; 込んて、しまうという手法がある。 こうすれ 前者は添字て、要素にアクセスし , 後者は名 typedef double point t [N axes] ; ば全体として値をやり取りすることもて、き 前て、要素 ( メンバ ) にアクセスするという差 るし , また各要素のアクセスに添字を用い そしてもうひとつの解として構造体を用 がある。あるデータ構造を表現したい場合 いる場合にはこのように書ける。 てループを利用することが可能となる。 に , 要素の型が異なっていれば構造体を採 / * 構造体の場合 * / / * 配列を構造体の中に埋め込む * / 用するしかなく , 選択の余地はない。しか typedef struct { enum{X axiS,Y axiS Z axiS,N axes}; し , たまたますべてが同じ型て、あった場合 typedef struct { はどうだろうか。そのような場合に配列を double x ; double data [N axes] ; 使うべきか構造体を使うべきかは非常に悩 double y ; } point t ; ましいところて、ある。 double z ; } point t ; たとえば , 以下のようなケースを考えて もっとも , 筆者は必ずしもこのようなコ ーディングを推奨するものて、はない。構造 みよう。 このふたつの技法の優劣を論じてみよう。 3 次元グラフィックスて、 , 空間内の点を表 座標データの場合には , x, y, z それぞれ 体を使って , いささか冗長になったとして も素直にメンバ名ごとの処理を書き下ろす す型として point t を typedef によって定義す の軸のデータに関して , まったく同一の操 ほうがいいかなと現時点て、は考えている。 るものとする。 x, y, z の三つの座標軸を用 作を施すケースが比較的多いと思われるの 汎用のマクロ定義例 List 1 : #include く stdio. h> 2 : 3 : #define swap(xp, (P) do { struct ー f00 ( char _bar[sizeof(*(xp))] ; } -t; 4 : *(struct ー f00 * ) (xp) ; 5 : *(struct ー f00 * ) (xp) = *(struct _foo*)(yp) ; 6 : *(struct ー f00 * ) (yp) -t; } while ( の ; 7 : 8 : 9 : #define writeln(x, fmt) printf()x ” 10 : int main(void) 12 : ( = 123 int X 14 : int y = 456 15 : double xd 2. 71827 ; yd = 3.14159 ; double xa[20] ニ ” He110 , world. 17 : Char yaC20] = ” Good-night baby. 18 : Char swap(&x, (Y) ; 20 : swap(&xd, &yd) ; 21 : swap(&xa, &ya) ; 22 : writeln(), Xi); 23 : writeln(), Xi); 24 : writeln(xd, % 7. 25 : writeln(yd, % 7. 26 : writeln(xa, (s) ; writeln(ya, (s) ; 28 : 29 : return 0 ; 30 : } ANSI C ー more 113

5. 月刊 C MAGAZINE 1992年2月号

X 68 k 活用講座 ング期間に再度スキャンにくるような高速 処理を行った場合に弾が余計に発射された り , 「テトリス」て劣化したスティックを使 っていると , 1 度てなく 2 度回転する現象が 現れたりします。 ハードスイッチをスキャンする場合には , このチャッタリングに対する考慮を忘れて はいけません。 X68000 のキーポードスキャ ンは別の専用 CPU が装備されていますの て、 , キーポードをリードする場合にはとく にこのようなことは考慮する必要はないと 思います。 List 4 は自機の移動を行う関数て、す。 1 / 60 秒に 1 ドット単位ての移動は意外に遅いも のて、す。て、すが , いつも 2 ドット単位の移動 てはおもしろくなさそうなのて , 少し工夫 して , 前回と同じ方向に移動するならば 2 ド ット単位て、移動するようにしてみました。 そのためにふたつの static な変数を用意して 管理しています。前述の移動方法なら同 一方向カウンタクは必要ないのてすが , 慣 性のある動きなどを作成する場合に便利な のて , 後の変更を容易にするために用意し ておきました。 8 行目に一見奇怪な記述があ ります。 enum 移動方向移動方向 , これは列挙移動方向〃てある変数ヾ移動 方向〃の宣言てす。 List 1 の 12 行目て、宣言さ れた enum のタグ名称ヾ移動方向〃と , 変数 として宣言する、、移動方向クとはまったく 別の名前として認識されます。 ANSI ては構 造体 , 共用体のメンバ名 , タグ名 , 通常の 変数名と衝突しないとされているからてす。 XC Ver. 1 て、はこれが実現されていなくて , bison などの一部の GNU ソフトウェアがコン パイルてきないことがありました。さすが に XC Ver. 2 ては直っています。 List 5 は敵キャラクタを移動処理する関数 てす。敵キャラクタは , 移動して画面外に 出たために消去される場合と , 自機に撃ち 落とされて ( この場合にはキャラは爆発パタ Fig. 2 LiSt 1 : / * 敵キャラクターの移動処理を行う。もし消去されたら 1 を返す。 敵キャラクタを移動処理する関数 0 2 : 5 : 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 18 : 19 : 20 : 22 : 23 : 24 : 25 : 28 : 29 : } “ A " を見えなくする処理を通常処理が忘れたためのバグ return 0 ; display-sprite ( 対象 , 非表示 ) ; else if (SPD-POS_Y ( 対象 ) = = 272 ) return 1 ; delete—sprite ( 対象 ) ; if (SPD_POS_Y ( 対象 ) = = 273 ) move—sprite_diff ( 対象 , 0 , 1 ) ; else return 0 : display-sprite ( 対象 , 非表示 ) ; else if (SPD-LIFE ( 対象 ) = return 1 ; delete—sprite ( 対象 ) ; if (SPD-LIFE ( 対象 ) = の SPD_LIFE ( 対象 ) + + ; if (SPD-LIFE ( 対象 ) くの 3 : 敵移動処理 (Sprite 対象 ) static int PIayer 0 1 0 1 A 実際画面 A あれ ? “ A ″が 動かないゾ Player 実際画面 0 ほれ , 帰線期間 1 だ , 表示だ 割り込み処理 0 俺 , 悪く 1 ないもん 割り込み処理 内部ワーク 内部ワーク 0 駅 A ″は死んだから X68k 活用講座 143 通常処理 1 なっている 0 消したつもりに 通常処理 1 消しちゃえ ! !

6. 月刊 C MAGAZINE 1992年2月号

djgccå* 解講座・ ll 0 刪 に移動します。 (void) fseek (fp, と同等て、す。 ftell.c OL, L SET) ; long ftell(FlLE *fp) ; fp の指すストリームのバッフアを , 標準の void setbuf (FILE * fp, char * buf) ; setbuf. c を返します。失敗したら一 1 を返します。 fp の指すストリームの現在の読み書き位置 バッフアから buf の指す領域に切り換えま す。 バッフアのサイズは , stdio. h て、定義され ている BUFSIZ ( 標準のバッフアサイズ ) にな ります。 標準のバッフア領域は解放されます。 buf として NULL を指定すると , バッファ リングをしなくなります。 stdio. h< は setbuf ( ) , setbuffer ( ) , set linebuf( ) の返り値がそれぞれ int と定義され ていますが , 実際には意味のある値は返し ません。 void 型として扱うべきて、しよう。 setbuffe. c void setbuffer(FlLE * fp, char * buf, int size) ・ 新しいバッファ buf のサイズを size て、指定 すること以外 , setbuf( ) と同様て、す。 void setlinebuf (FILE * (p) ・ 行単位のバッファリング ( 改行が生じる と , バッフアの途中て、もデータをフラッシ ュする ) をするよう指定します。コンソール のノヾッファリングに向いています。 バッフアのサイズは BUFSIZ になります。 る場合がある。また , 変化しないように見える変数 に命令が副作用をおよばす場合 , あとて、たまたまレ ジスタに見つかった古い値が使用されるかもしれな asm 命令が削除 , 移動されたりまとめられたりする のを防ぐためには asm の次に予約語 v 引 at ⅱ e を書けば asm volatile ("set priority % 0 ' #define set priority (x) なければならない 書いている場合には , asm の代りに asm を使わ ANSI C からインクルードされるヘッダファイルを ドがないためこの問題は起こらない 通常の「テスト」および「比較」命令ては出力オペラン れらの格納命令は条件コードを変化させてしまう。 が必要となるのが問題て、ある。多くのマシンてはこ 格納を必要とするて、あろうし , その場合に格納命令 ことに気がついた。出力オペランドは変数などへの それを実現しようとして我々は安定した方法がない スしたいと思うのはもっともて、ある。しかしながら アセンプラ命令の結果て、ある条件コードをアクセ いて削除や移動されたりすることはない ) ( ただし , 出力のない命令は制御が届かない場合を除 / * no outputs * / \ 5.17 アセンプラ上ド用前の制御 C の関数や変数のアセンプラコード中て、の名前を指 定することカイきる。次のように宣言子の後に asm ( または asm ) を書けばよい int f00 asm ("myfoo") これは変数 f00 がアセンプラコード中て通常の ' fo 0 ' の代りに myfoo' という名前て、呼ばれることを指定 する。 C 関数や変数の名前に通常アンダースコアを付ける ようなシステムては , この機能によってアンダース コア以外て、始まる名前を定義してリンカに渡すこと がて、きる。 関数定義に対してはこの asm は使用することカイき ない。同じことをするには次のように関数定義の前 に asm を使った関数宣言を書けばよい。 extern func ( ) asm ("FUNC") ; 指定された名前がアセンプラのほかのシンポルと 衝突しないようにするのはユーザの責任てある。ま た , レジスタ名を使用してはならない。その場合に はまったく動かないアセンプラコードが生成されて しまう ( * 6 ) 。 GNUCC は静的変数をレジスタに割り付 ける機能がまだない ( いつか加えられるかもしれない が ) 。 func (), y) int x, y , * 6 メモリ参照のアドレッ シングモードを使ってレジ スタを参照するコードが出 力されてしまい , アセンプ ル段階てエラーが生じる可 能性があります。 djgcc 詳解講座・ H 0 GCC World 81

7. 月刊 C MAGAZINE 1992年2月号

COMPUTER LANGUAGE AUGUST 1 991 ド am 血い暉咄 監 富 『 Watching the watchers 』 くだらないテストよりはるかに大きな信頼 次に自分自身て、テストしなければならな ースト産業の出現 が持てるだろう。だからテスト設計のため い場合 , テストプログラムを選択しなけれ の特別な技術は尊敬に値するのて、ある。 ばならない たとえば C コンパイラの ANS テストは今や産業の重要な一部となった。 I / ISO 標準適合度テストて、は , 数社のべンダ 私は複雑なソフトウェアを書くのが好き ソフトウェアの顧客 , すなわちユーザ ( およ ーがテストスイートを提供している。 C コー だ。長い間 , いろいろなコンパイラやオペ びソフトウェアベンダー ) の増大につれて , ドの移植性が高いかどうかをテストしたい レーティングシステム , そしてソフトウェ 新しい現象が現れた。つまり , ューザが選 アツールを書いてきた。それは簡単な仕事 のなら , 各種の商用ツールがソースの行ご 択肢を持てるようになったのだ。これまて、 とに判定してくれる。 PDS のものさえあ て、はなく , 期待したほど成功しなかったも カスタムパッケージを作ってもらおうとし のもある。て、も楽しい仕事だったのは確か る。あなたはどれに時間と金を投資するか ても , どの会社が信頼て、きるのかという選 だ。複雑なソフトウェアを書く際に私にと を決定するだけて、いいのだ。 択基準しかないのが通常て、あった。また市 コンノヾイラやオペレーティングシステム って一番つまらないのはソフトウェアのテ 販のパッケージを選ばうとしても , せいぜ の場合とは異なり , テストて、は複数のツー ストだ。世の中にはテストのコツを知って い 1 社しかべンダーの候補がなかった。しか ルを利用することも可能だ。作者の異なる いる人もいるのだろうが , 私は知らない 誰かにもっと完全なテストパッケージを書 し今て、は共通の規格に適合したソフトウェ ふたつのテストスイートによるテストは , アを提供しているべンダーが 3 ~ 30 社という 同じようなテストパターンを百万回も繰り いてもらうほうがよい のも , 決して珍しいことてはない 多くの場合 , テストパッケージはテスト 返すのとは違うはずだ。たとえふたつのチ ェック内容が同じだとしても , カバレッジ されるパッケージと同程度に複雑て、ある。 ーストの監視者 一般的にテストスイート (testsuite) と呼ば が違っている。そのため複数のテストパッ ケージを使うと , 単独のパッケージ使用時 れるものは多数の小さなテストて、構成され それて、はどうやって選択すればよいのだ よりもっと多くの問題が発見て、きる。他の ている。小さなテストそれぞれは非常に単 場合と同様にテストの場合も , この特別な 純なものだが , 包括的なテストスイートと ろうか。比較経験を持っている人を発見て、 きれば幸運だ。 TCOMPUTER LANGUAG 分野に対して投資て、きるリソースの種類と するまて、にはたくさんのテストが必要にな る。しかも各テストがうまく協調して動作 E 』などの雑誌には , プログラム開発に必須 量て、限界が決定される。 価格が問題なのて、はない。大規模な商用 なプロダクトの比較記事が頻繁に掲載され しなければならない。テストは量 ( 回数 ) が ている。このサービス ((COMPUTER LA のテストパッケージは決して安価て、はない 多ければよいというわけてはない くだら N GUAGE 』誌 Programming on purpose ないテストばかりを何億回も繰り返すだけ が , それだけの金額を支払う価値はある。 のプログラムなら誰にて、も書ける。優秀な May 1989 、、 What you see is what you g つまり , うまく構築されたテストー式と , テストプログラムは数千程度の組み合わせ et 〃参照 ) は貴重だが , 内容を最終的に判断 それらの設定と使用に関する有用なサポー て、 , はるかに多くの意味のある変化を提供 するのは自分自身て、ある。通常 , 比較記事 トが入手て、きるのぞある。一方 , 無料 (free) て、きる。後者のようなプログラムのテスト の基準と方法に信頼がおけるなら , その結 パッケージの場合 , 使い始めるのが困難な ノヾ にパスてきれば , 前者のプログラムによる 果にも信頼がおける。 ことも , バグがあることもある。フリー 15 Programmi ng on Purpose

8. 月刊 C MAGAZINE 1992年2月号

/ * 引数が即値の場合の %con port Table 4 cc から起動されるツール 関数本体の定義 * / 内容 ツール プリプロセッサおよびコンノヾイラ /usr/ccs/lib/acomp オプティマイザ /us 「 /ccs/lib/optim のように , asm 関数として定義します。 基本プロックアナライサ ( ー p などで使われる ) この /usr/ccs/lib/basicblk とき , 引数のストレージモード , つまり reg アセンプラ /us 「 /ccs/bin/as ister か , メモリ上か , それとも即値かなど /usr/ccs/bin/ld リンクエテイタ の条件によって , 実行する命令を指定する TabIe 5 べンチマークテスト結果 ことがて、きます。たとえば , outb 命令の場 「 egister 合には , 一度 AX / EAX へ出力すべき値を口 Machine/compiIer int ードしてこなければなりません。レジスタ UNIX-svr4/386 ( ー 0 ) 0. 10 同士の場合ならば , UNIX-svr4/386()o -0 ) 0. 1 2 movl val, %eax TabIe 6 他マシンでの測定結果 のように , 無条件に 32 ビットコピーしても かまいませんが ( メモリの節約になります ) , 「 egiste 「 auto auto lnt a utO Machine/compiler int short ng multiply dbl オペランドがメモリ上にあった場合には , movb val, % 引 C 「 ay X-MP()o vecto 「 s) 0.0567 0.0656 0.0822 0.366 0.821 0.082 Sun 3 / 75M ( 4.2 , -0 ) 0.50 0.81 0.83 2.85 20. 7 のようにきちんとバイトコピーを行わない UN Ⅸー sv 「 4 / 486 ( ー 0 ) 0. 10 0. 1 8 0. 1 0 0.33 0.63 と , 場合によってはメモリ参照例外が起き ることがあるのて、注意が必要て、す。 %reg port ; mem val ; スクに収録されていたべンチマークテスト このストレージモードには , のように ; ( セミコロン ) て、区切って記述て、 を実行してみました。そのサマリを Table 5 コンノヾイラがイ吏用しているテン きます。 に示します。 treg また , asm 関数のネストした呼び出しはて、き ボラリレジスタ オプティマイズのあるなしてあまり極端 ませんのて、 , 呼び出し時に注意が必要て、す。 な差は見られませんが , register int, auto コンパイラが割り当てた regis もうひとつ , SVR4 / 386 のアセンプラは , long, func call て、一 0 オプションっきのほう ter 変数 が優秀な成績を示しています。 treg または ureg インテル系 CPU て、ありながら , オペランド reg 即値 の順序は , registerint について -S て、出力したアセン con プラリストを比較してみると , 明らかにム メモリ opecode source, destination mem ダなレジスタのロードが削除されているよ 1 ab という , VAX や 68000 風の形式を採用してい ラ / ヾノレ 引数のストレージモードが定義 ます。生粋の MS ー DOS 出身のプログラマは うて、す。たとえば , error きっとしばらくまごっくて、しようから , ち ① a = a 十 b 十 c ; されていない場合工ラーを起 よっと頭の片隅て、意識しておく必要がある ② b = a > > 1 ; こさせる て、しよう。なお , 即値は $ を , レジスタは % の 6 個 ( error を入れると 7 個 ) が用意されてい ③ a = b % 10 ; ます。ストレージ条件指定は , をプレフィックスとしてつけます。 の一連のコードて、は , ①式て、変数 a に値が代 ストレージモードバラメータ 入されていますが , その値は②式てるの代入 コード生成 のようにして行います。ひとつの条件は , 計算にそのまま使われた後 , ③式て、 a に違う 次の条件が出てくるまて、有効て、す。たとえ 値が代入されているのて、 , ①式において変 ば , 引数 val が reg の記述は , 今回はコード生成 , オプティマイズとい 数 a への代入は必ずしも必要て、ないことがわ かります。この部分のアセンプラ出力を比 %reg val った項目についても見てみます。一応 , cc movl val, %eax には -O というオプティマイズ用オプション 較したのが Fig. 6 て、す。 が用意されてはいますが , いったい , Fig. 6 に見られる最適化などほんの些細 どの な差と 0 、えるかもしれませんカ : , この一連 程度オプティマイズされるのて、しようか ? となります。 複数の条件を指定する場合は , まずは , C マガジン ' 90 年 10 月号の付録ディ の式はループのもっとも深い部分て、出てく func call i nt multiply autO long 0. 1 0 0. 1 2 auto short 0. 1 8 0. 1 8 a utO dbl 0.63 0.58 0.33 0.66 fu nc call ureg 96 C MAGAZINE 1992 2

9. 月刊 C MAGAZINE 1992年2月号

たまたま最初にインプリメントされたコ ンノヾイラカ istructure equivalence を採用し ていたため , 一時期 Pascal て、はこちらが主 流だったようて、ある。しかし , 現在て、は IS O-Pascal の規格て、 name equivalence が採 用されたし , それ以外の言語て、も Ada を始め としてこの流儀が主流になっている。 C もそれに合わせたわけて、ある。別の型だ からこそ別の名前を与えるわけて、あって , 名前が違えば異なる型というほうが論理的 かっ安全だし , コンパイル時の処理も楽に なるため , これは望ましいことだと思う。 先ほどのタグ名なしの宣言に戻るが , 結 局タグ名なして、構造体を宣言してもその場 て、同時に宣言した変数て、しかその型を利用 て、きない。したがって , 実質的にはこのよ うな使い方はされないと思われる。それて、 はなぜタグ名なしという構文が許されるの かというと , それは typedef することを想定 void f00 (void) f00 t m f00 t n ; } f00 t ; long zot ; double bar ・ typedef struct { したからて、あろう。 108 C MAGAZINE 1992 2 出現する ( タグ名は共用体の union や列挙の 意してほしい。タグ名とは必ず struct の後に 名て、あってタグ名とは呼ばれないことに注 る。 typedef を行った場合 , f00 t は typedef することがて、き , 当然それらは同じ型とな t を型の名前として用いて変数 n , m を宣言 ため , タグ名なして、も間題は生じない。 f00 れには f00 t という名前が与えられる。この ef て、タグ名なしの構造体を使っているが , そ 上記は typedef の例て、ある。最初の typed enum の後にも出現する ) 。 f00 t は struct の 前置を必要としない それならば , typedef するときにはタグ名 を指定する必要はまったくないのかという と , そうて、もない。自分自身と同じ型への ポインタをメンバに含む , いわゆる自己参 照的な構造体の場合には typedef しつつタグ 名も指定したほうがよい場合がある。 たとえば , 片方向のリンクて、チェインさ れるようなデータ構造を考えてみよう。そ れぞれのエントリは前もって typedef された info t 型のデータを持っているものとする。 struct slink info t info * next , struct slink { struct slink info t info * next , すなわち最初に slink というタグ名をとに かく宣言してしまう。この時点て、は構造体 の中身は不明て、ある。ところがこのように タグ名だけを宣言することがてきる。なお , ANSI には「 incomplete type 」 ( 不完全型 ) という概念上の型があり , これはその一例 て、ある。その後に実際の内容を宣言するの て、ある。この例て、はなぜこれが有効なのか が理解て、きないだろうが , ふたっ ( 以上 ) の 構造体がお互いにポインタて、参照しあうよ うな , いわゆる相参照的な構造体を・ド する場合には必須となる。 この場合 , next というメンノヾが次のエン トリへのリンクとして機能する。この場合 には typedef を用いても次のように書く必要 がある。 typedef struct slink { struct slink * next ; info t info ・ } slink t ; こて、 next を指定する時点て、はまだ slink t という typdef 名は宣言されていない。した がって ,next の宣言を slink t *next と記す ことはて、きないのだ。このため structslink という指定が必要になる。 なお , slink t のように typdef 名の末尾を t て、終えるのは単なる習慣て、あり , 別に文法 上定められているわけて、はない 最近て、は広く用いられているし , 型の名前 て、あることが一目瞭然なのて、この習慣に従 うことをお薦めしたい もうひとっ , 例外的な構造体宣言の技法 を紹介しておこう。それはいわば forward 宣 言に相当するのだが , 実は次のような記述 が可能て、ある。 struct slink ・ struct slink { struct bar ・ struct f00 { struct bar f00 info t struct bar { struct f00 bar info t infO infO この例て、は , struct f00 がメンバとして s truct bar へのポインタを含み , struct bar はメンバとして struct f00 へのポインタを含 んて、いる。このため , struct f00 , struct b ar いずれの宣言を先に記しても未定義な構 造体へのポインタが出現してしまう。 Pasc al て、はそのようなケースに備えて , 具体的な 型が未定義て、も , その県へのポインタとい う型のメンバは宣言可能とするという便宜 的な措置が取られている。しかし , C て、はそ のような処理は行われない。その代わりに C にはタグ名のみを宣言してしまう機能が備 えられたと考えるべきて、ある。 Pascal とは違って , このような機能を備 えた理由は , 上記の例のような相互参照的 な構造体をネストしたスコープの内側て宣

10. 月刊 C MAGAZINE 1992年2月号

ゲームの仕様と設計 吉野智興 先々月号は思わぬ台風災害のためにお休みしてしまいま した。今月から , お約束の簡単なスプライトゲーム作成に 入ってみます。非常に簡単なゲームですか , スプライトを 使ったゲームでの基本的な手順は把握できると思います。 6 k; 用講座 GCC で子ぶ X68 ゲーム プログラミング 第 8 回 仕様 仕様はノンスクロール ( 将来背景を入れる かもしれませんが ) のスプライトだけを使っ たインべーダゲームもどきてす。画面上方 からランダムに発生するキャラクタを自機 が射つ , それだけのゲームて、す。あまりに 簡単なのて拍子抜けするかもしれませんが , 画面の初期化やファイルリード以外はいっ さいライプラリコールを使わないのて , ソ ースはけっこうな分量になります。今月は そのソースの一部分の設計とそのほかの雑 知識を紹介します。 スプライトの制御には , 11 月号て、使った sprite. c に若干の関数を追加したルーチンを そのまま使います。「手抜き」とともに「ソフ ト資産の活用」を行うわけてす。一度基本的 な処理をライプラリ化しておけば , 上位の 処理に変更があった場合ても再コンパイル の手間や , デバッグ時間の短縮に大きな効 果があります。 実際のゲームては , 「見栄え」のほとんど はキャラクタデータの善し悪して決定して しまいます。多少動きが複雑ておもしろそ うても , 単純なアクションゲームてあった り , RPG の大作「ローグ」のように主人公が ヾ @ 〃て、は感情移入はおおよそ不可能て、す。 プログラムが工夫されていてもキャラクタ が貧弱ては興味半減なのてすが , 実際にド ット絵を作成するのは素人てある私には非 常に困難なのて , キャラクタのてきの悪さ は見逃してやってください。皆さんがゲー ムを作成なさる場合には , 目いつばいカッ コイイキャラクタをデザインしてください ね ( 注 1 ) 概略設計 List 1 は今回作成しているゲームプログラ ムの変数宣言部分て、す。漢字コードが変数 名やマクロに使われていますが , よくプロ グラム解説記事てみられるアルゴリズムを 説明するための日本語を混在させたリスト て、はなく , ちゃんと C マガジン付録の GCC て コンパイルてきる立派な C のプログラムて す。完全に ANSI を逸脱していますが , X6 8000 のアセンプラ・リンカは , 漢字コード をラベルとして認識してくれるのて、 , 付録 の X68000 版 GCC て環境変数を設定しておけ ば , 「識別子」が置ける場所にはどこにても 漢字混じりの名前を使うことがてきます。 XGCC ては , 最初から日本語のことを考え てソフトウェアを設計してあるのて , この ようなことが可能になっています。英語て 変数名を作ることによる綴りのミスや , う ろ覚えて、のエラー防止 , それとなんといっ ても圧倒的にプログラムの読み取りが容易 になるのて , 今回はこの日本語拡張をフル に活用してみました。まるて C のプログラム に見えないのは「お笑い」てすが , 何度もい いますけどちゃんとコンパイル・実行てき ます。漢字識別子の使用には , 環境変数の 設定が必要てす。 1 月号の記事をご覧くださ い。また 1 月号の記事に訂正があります。 sc d. x, gdb. x ては漢字を使った変数が評価て きません ( 注 2 ) 。プレークポイントなどは設定 てきますが , 評価てきないのては「絵に書い た餅」てしかないのて多少デバッグには不便 があるようてす。 話が脱線しました 0List 1 の 1 ~ 3 行は , い くつかのライプラリ関数のためにインクル ードするヘッダてす。 6 行目の sprite. h は , XC に添付されているヘッダてはありません ( 最初 " 〃を < > 〃としていて , 工ラーの 多発によって気がついた思か者は私てす ( 編 集部注 : 次号以降て収録します ) ) 。 ヾ絶対値〃マクロは当たり判定を行うため X68k 活用講座 139