lnformation from Compiler Makers に含めることがて、きます。 オーノヾレイマネージャのコード は , 標準ライプラリに含まれてい て , LINK コマンドライン上て、オー バレイ指定がされると , オーバレ イマネージャは自動的にほかのモ ジュールとリンクされます。また , オーバレイの中のルーチンへの far コールは ( 後にモジュール識別子と オフセットが続く ) 割り込みに置き 換えられます。デフォルトの割り 込み番号は 63 ( 0x3F ) て、す。この割 り込み番号を変更するには , / O オ プションを使います。 オーバレイはメモリの同じ場所 にロードされるのて、 , 1 回にひとつ のオーバレイしかメモリ上に存在 て、きません。異なるオーバレイに 同じ名前のモジュールがあっては いけません。ひとつのプログラム の中て、 , 各モジュールは 1 回しか現 れません。 オーノヾレイモジュール オーバレイを使う場合て、も , LI NK はひとつの . EXE ファイルしか 作成しません。このファイルは , オーバレイモジュールが必要とな るたびに , 繰り返し読み込まれま す。 実行ファイルが実行されると , オーバレイマネージャは , 別のオ ーバレイモジュールをロードする 必要が生じるたびに , この . EXE フ ァイルをサーチします。 オーバレイマネージャは , 最初 にカレントディレクトリを探しま す。該当するファイルを発見て、き ない場合は , PATH 環境変数て、設 定されているディレクトリを探し ます。それても発見てきなければ , ファイル名の入力を要求するプロ ンプトを表示します。たとえば , オーバレイを使う実行ファイル PA YROLL. EXE が , カレントディレ クトリにも , PATH て、設定されて いるディレクトリにも存在しない とします。この場合 (PAYROLL. E XE が存在するディレクトリ以外か ら , フルバスを指定して ) PAYRO LL. EXE を実行すると , オーバレイ ファイルをロードしようとした時 点て、 , オーバレイマネージャが Fi g. 3 のようなメッセージを表示しま す。 プログラムの実行を続けるには , こて、 , PAYROLL. EXE が存在す る場所を示すドライプ , またはデ ィレクトリ , あるいはその両方を 入力します。たとえば , このファ イルが B : YEMPLOYEEYDAT AY にある場合 , B : *EMPLOYEE*DATA* と入力するか , カレントドライプ が B なら , 単に YEMPLOYEEYDA TAY と入力します。 その後 , ドライプ B からディスク を取り外し , オーバレイマネージ ャが再びオーバレイをアクセスす る必要が生じた場合は , Fig. 4 のよ うなメッセージが表示されます。 オーノヾレイファイルがディスク から読み込まれると , オーバレイ マネージャは Fig. 5 のようなメッセ ージを表示します。 オーバレイプログラム 作成上の注意 ・プログラムによっては , オーバ レイを使用て、きない場合もあり ます。 ・ MS-DOS Ver. 2.11 以前て、は , オーバレイを含む実行可能プロ グラムファイルをリネームしな いて、ください 0LINK は , プログ ラムファイルに . EXE ファイル名 を記録します。ファイル名を変 更すると , オーバレイマネージ ャが正しいファイルを探せなく なります 0MS-DOS Ver. 3.1 以 降て、実行する場合はリネーム可 能て、す。 ・オーバレイされるモジュール間 て、制御を移すには , far コール / リ ターン命令を使わなければなり ません。ほかのモジュールがこ れらのルーチンを呼び出す場合 は , near ルーチンを含むモジュ ールをオーバレイすることはて、 きません。 ・オーバレイされるモジュールて、 は , longjmp ライプラリ関数を使 ってモジュールを出る , または モジュールに入ることはて、きま せん。ただしモジュール内て、あ れば可能て、す。 ・関数ポインタを使って , オーバ レイされるモジュールを出る , または入るルーチンを呼び出す ことはて、きません。ただしモジ ュール内て、あれば可能て、す。 一度にひとつのオーバレイしか 必要ないように , プログラムを 構成しなければなりません。 ・異なるオーバレイて、 , 同じパプ リック名を使うことはて、きませ ん。 Fig. 2 Fig. 4 Fig. 3 Fig. 5 オーバレイの指定 ( 凵 NK のく objf ⅱ es > フィールド ) a 十 (b 十 c) + (e + f) + 9 十 (i) オーバレイマネージャのメッセージ ( 2 ) Please insert diskette containing オーバレイマネージャのメッセージ ( 1 ) Cannot find PAYROLL. EXE Please enter new p 「 ogram spec ・ lnformation from Compiler Makers 149 Strike any key when ready. Please restore the original diskette. オーノヾレイマネージャのメッセージ ( 3 ) and strike any key when ready. B : *EMPLOYEE*DATA*PAYROLL. EXE in drive B .
マイクロ、 . 厚ト lnformation from Compiler Makers 今回は , MS-CVer. 6.0 の LIN K. EXE に関する機能として , 応答 ファイルの利用とオーバレイプロ グラムの作成について解説いたし ます。なお , これらの機能の解説 は , MS-CVer. 6.0 付属のオンラ インヘルプから参照することが可 能て、す。 応答ファイルとは MS-CVer. 6.0 を使って , プロ グラムを開発する場合 , PWB や M AKE ファイルを使用することて、 , 実行ファイルを自動的に作成する ことがて、きます。しかし , このほ かにもリンクの設定や複雑なリン ク作業の手順を保存して , この手 順に従って LINK. EXE を実行する 方法があります。 MS-C Ver. 6.0 に付属のリンカ て、は , 応答ファイルに LINK. EXE の行う作業を記述して , LINK. EX E の実行時に , この応答ファイルを 指定することにより , 一連の作業 を手続き化することがて、きます。 応答ファイルの指定 応答ファイルを使って LIN K に 指示するには , DOS のプロンプト て、次のように入力します。 凵 NK @responsefile <responsefile> フィールドには , コマンドラインまたは LINK プロン プトに対する入力と同じ内容が記 述されたファイルの名前を指定し ます。このファイルて、は , それぞ れの入力項目は別の行に置くか , カンマて、区切ります。 <responsefi le > フィールドに指定て、きる応答フ 応答ファイル 148 C MAGAZINE 1992 6 アイルはひとつだけて、す。 ! 特別な機能 ・ LINK コマンドラインまたは LIN K プロンプトのどの場所て、も , そ こから後の入力を応答ファイル を使って行うことがて、きます。 応答ファイルに , 残りのフィー ルドやプロンプトに対するすべ ての入力事項が含まれていない 場合 , LINK は不足分に対するプ ロンプトを表示します。 ・応答ファイルて、は , キーポード から入力する場合と同じ方法て、 , 特殊文字を使用て、きます。たと えば , プラス記号 ( 十 ) を使って 改行したり , セミコロン ( ; ) を 使って残りすべてのプロンプト に対してデフォルトの応答を選 ぶことがて、きます。 ・ LINK は , 画面上にプロンプトと 応答ファイルからの入力内容を 表示します。応答ファイルから の入力が受け入れられない場合 , LINK はプロンプトを表示して応 答を待ちます。ただしソ B 〃オプ ションを使うと , このプロンプ トは表示されません。 ・オプションは , 応答ファイル中 のどこにあっても構いません。 応答ファイルによるリンク例 応答ファイルの例を Fig. 1 に示 します。応答ファイル名を FUN. L NK とした場合 , コマンドは次のよ うになります。 LINK @FUN.LNK この場合 , LINK は次の一連の動作 をします。 1. FUN, TEXT, TABLE, CAR E の 4 個のオプジェクトモジュー ルを FUN. EXE という名前の実 行ファイルにリンクします。 2. FUNLIST. MAP というマップ ファイルを作成します。 3. 実行ファイルを作る前に一時停 止します。ここて、ディスクを交 換することもて、きます。 4. パプリックシンボ、ルとアドレス をマップファイルに入れます。 5. , ライプラリファイル GRAF. LIB から , 必要なすべてのルーチン をリンクします。 オーバレイプログラム の作成 GRAF. L 旧 FUNLIST /PAUSE /MAP FUN TEXT TABLE CARE Fig. 1 応答ファイルの例 ります。 ードが必要なため , 実行は遅くな が , ディスクからのロードや再ロ 使うとメモリが少なくて済みます きません。一般に , オーバレイを ドだけて、 , データはオーバレイて、 とき , オーバレイて、きるのはコー を作成することが可能て、す。この いメモリ上て、動作するプログラム 分を共有することによって , 少な 数のモジュールがメモリの同じ部 MS-C Ver. 6.0 の LINK は , 複 ロードされます。 必要になったときにだけメモリに プログラム中のコードの一部は , る処理を行う方法て、す。この場合 , メモリ上て、 , 大量のメモリを要す 分を共有することによって少ない オーバレイは , メモリの同じ部 オーノヾレイとは MS-C Ver. 6. O メモリモテル オーバレイが使えるのは , ミデ イアム , ラージ , ヒュージモデル プログラムだけてす。スモールお よびコンパクトモデルプログラム て、は , 異なるモジュールのコード はひとつのセグメント ( C て、は TE XT セグメント ) に置かれます。 ディアム , ラージ , ヒューシモデ ルて、は , 各モジュールは独自のセ グメントを定義します。オーバレ イを機能させるためには , 別々の セグメント名が必要て、す。 ! オーノヾレイの指定 オーバレイを指定するには , 複 数のオプジェクトファイル名をカ ッコて、囲んて、指定します。カッコ て、囲まれたオプジェクトファイル のグループは , ひとつのオーバレ イを表します。たとえば , LINK コ マンドラインの < objfiles > フィー ルドに , Fig. 2 のようにオプジェク トファイル ( ライプラリも可能 ) の リストを指定します。 Fig. 2 の例て、は , モジュール ( b 十 c), (e 十 f), および ( i ) がオーバレ イて、す。残りのモジュール ( a と g), および <libraries> フィールド のライプラリからのモジュールは , プログラムの実行中ずっとメモリ に常駐したままとなります。ま た , <objfiles> フィールドに指定 されたライプラリも , オーバレイ
て , Fig. 4 のようなことがいえる。すなわ ち ,struct 全体のサイズは各メンバそれぞれ のサイズの総和を下回ることはない。等号 が成り立つのは , 構造体内部にアライメン ト調整のための隙間 ( 穴 ) が存在しないとき て、ある。 一方 , 共用体の場合にはそれぞれのメン バのうち , 最大のサイズを有するものと同 じサイズを下回ることはない。この場合も 同様に共用体の末尾部分にアライメントの 調整の隙間が存在するかもしれないのて、 , 不等号はそのような場合のためて、ある。共 用体の末尾に隙間が生じ得る典型的な例は 次のような宣言て、ある。 union baz { short a ; b [ 3 ] ; Char 共用体のサンプルプログラム List 1 : #include く stdio. h 〉 2 : 3 : int main(void) 6 : 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 16 : } volatile union zot { long float y; u. x = 1 ; u. Y ニ 1.0 ; printf( ” u. x ニ Xd}n" u. x) ; u. x ニ 1 ; printf("u. y = Xf}n", u. y) ; return 0 ; が独立した変数て、あり , それらをひとまと めにして管理するための言語機能て、あった。 この場合 , Table 1 のように short のサイ これに対して共用体の場合 , 各メンバはそ ズが 2 バイトて、あるとして , かっコンパイラ れぞれ同じメモリアドレスから割り付けら が共用体全体のサイズを偶数にアライメン れるため , その存在はお互いに独立て、はな トするような場合には , この unionbaz のサ 。たとえば , List 1 のようなプログラムを って暗黙のうちに異なる値に変わってしま い イズは 4 バイトになるだろう (Fig. 5 ) 。もち 考えてみよう ( volatile は最適化の結果 , 思 う。実際に手元の MS ー C 6.0 て、試してみたと ろん , コンパイラによっては末尾の pad は付 わぬ結果になってしまうのを予防するため ころ ,Fig. 6 の結果を得た。ちょっと考える 加されず , 全体のサイズは 3 バイトのままて、 に念のため付加している ) 。 と , これて、は共用体は安心して使えないて、 ある場合もあり得る。 List 1 を実行させたとき ,printf の呼び出 はないかということになる。 共用体の変数に対してメンバづけを行わ しによって出力される u. x の値はおそらく 1 て、 しかし , 実際にはこれによって共用体の ずに , 共用体全体として代入などを行うこ はないだろうし , u. y の値もおそらく 1.0 て、は 有用性が損われるということはない。これ とは可能て、ある。また関数の引数に共用体 ないだろう。具体的にどのような値になる は本来そのおのおののメンバが排他的にし 全体を渡すこともてきる。構造体て、もこれ かは処理系依存て、あるが , 要するに u. y への か存在しないようなケースに用いるための らの操作は可能なのて、あるから , これは当 1.0 の代入によって u. x が割り付けられていた データ構造なのて、ある。 然のことといえる。 メモリ領域への書き込みが行われるため , たとえば , コンパイラの前段フェーズて、 K & R 時代の古い C 言語て、は共用体変数の u. x がその時点て、保持していた値は上書きさ ある字句解析部を考えてみよう。字句解析 初期化は許されなかったが , ANSIC て、は初 れてしまい , u. x の値は 1 て、はなくなる。 部の一般的な機能は , ソースファイルを読 期化も許される。その場合 , どのメンバに 同様に , u. x への 1 の再度の代入によって u. み込んて、 , 文字の並びを所定のトークン単 対して初期化を行うかが問題となるのだが , y が割り付けられているメモリ領域の一部が 位に区切るとともに , 各トークンの値を何 これは「先頭に宣言したメンバ」て、あると定 書き変わるため , u. y の値はもはや 1.0 て、はな らかの内部表現に置き換えて次段のフェー められている (ANSI 3.5.7 ) 。 くなってしまうのて、ある。 ズて、ある構文解析部に送るというものて、あ 上書きした値のビットパターンがたまた 体の利点 ま元々の値のビットパターンと同じて、あっ この場合 , たとえば数値定数を読み込ん たという希有な場合を除けば , u. x および u. だ場合に , それが整定数て、あれば long 型の 構造体というのは , 各メンバがそれぞれ y の値はお互いに他方のメンバへの代入によ 値を , 浮動小数定数て、あれば double 型の値 Fig. 6 List 1 の実行結果 u. x : 0 u. y ニ 0 ℃ 00000 114 C MAGAZINE 1992 6
コレ・エス・アイジャハン lnformation from Compiler Makers LSI C -80 算関係のときは 80X87 の有無 する場合には , 問題が再現する最 Q 5. コンパイル時のエラーのときに 小の大きさまて、プログラムを絞っ LSI C ー 80 で enum を使用して L は , 正確なエラーメッセージ ist 1 のように記述すると Fig. 1 の てください。 60 行を越えるリスト 上記の 2 , 3 , 5 については , 手書 ような警告工ラーになります。な 製品に添付されている「登録カー はフロッヒ。ーて、送ってください きよりもハードコヒ。ーのほうがよ ド」に必要事項を記入したうえて早 長いリストのハードコピーを送ら ぜでしようか ? り正確に情報が伝わります。たと れた場合には , 回答に時間がかか めに返送をお願いします。 A ANSI て、は列挙定数は整数定 「登録カード」が返送されていな えばスペルは間違いがなくても大 ることがあります。 数と同じ扱いがて、きるとなってい 文字 / 小文字の違いにより状況が大 いと , 技術サービスを受けること パソコン通信をされている方は るのて、警告工ラーは出力されない がて、きない場合がありますのて、ご きく変わる場合などもあります。 下記 ID まて、 , 電子メールて、送って ほうが望ましいて、しよう。 LSI C- より正確な情報によって回答もよ いただいてもけっこうて、す。 了承ください 80 も次回のバージョンて、は警告ェ また , 返送された「登録カード」 り速くて、きます。 NIFTY-Serve ラーが出力されないように変更す をもとに新製品情報 , 他製品特別 また , プログラムリストを添付 NAB00437 村木多乃美 る予定て、す。 割引価格のご紹介 , ノヾーーションア Fig. 1 enum の警告メッセージ List 1 の例のように #define する ップのご案内などをお送りします。 Warning : enum type mismatch ( = ) 代わりに enum を使用て、きますが , Fig. 2 switch 文のバグによるエラーメッセージ List 2 の例のように enum 変数を使 用することもて、きます。 cg e 「「 0 「 ()o match) 技術サービスをご利用になると List 2 の enum 変数 x には enum きには , 製品に添付されている「技 enum を使用して警告工ラーとなる例 C010r 型の列挙定数 , または enum 術サービスカード」をコピーし必要 enum boolean { NO, YES } ; 変数以外を代入すると警告ェラー 事項を記入のうえファクシミリま func() になります。したがって , List 2 の たは郵送にてお送りください。電 i nt ように enum を使用して記述するこ 話による質問はご遠慮ください とて、 , より信頼性の高いプログラ 技術サービスカードには次の事 ムを作成することがて、きます。 項を必ず記入してください ノヾ - ーション : 番・号、シリ 1 . 製品名 , アル番号 Q 2. 会社名 , 担当者名 , 電話番号 , LSI Cー80 で List 3 のようなソ ファクシミリ番号 , 使用機種 ースをコンバイルすると Fig. 2 のよ うなエラーが発生します。なぜで 質問内容は製品に関することに しようか ? 限ります。 C 言語そのものや MS-D OS などに関するご質間はご遠慮く A コンノヾイラのノヾグのために工 ださい 技術サービスには問題が発生し ラーになっています。 List 3 のプロ グラムのように switch 文の式が定 たときの実行環境を詳しく明記し 数て、あったり , 型変換 ( キャスト ) てください されている場合に Fig. 2 のような工 たとえば , 次の事項て、す。 ラーになります。もちろん , 言語 1. 実行時の使用機種 ( ROM 化して 仕様て、は定数て、あっても型変換さ いる場合は CPU の種類 ) 2. 与えたコマンド れていても問題ありません。 このエラーを回避するには定数 3. LSI C ー 86 の場合 lcc, LSI C- 80 の場合は lsic80. cfg の内容 や型変換したものを一時変数に代 4. LSI C ー 86 の場合て、浮動小数点演 入してその変数を switch 文の式に 152 C MAGAZINE 1992 6 してください 登録カード 技術サーピスの利用方法 List 1 re tval ; / * 警告工ラー * / retval : NO; enum 変数使用例 boolean { NO, YES } ; enum C01 or enum BLACK, BLUE, RED, PURPLE, GREEN, SKYBLUE, YELLOW, WHITE C010r x; enum int y; func ( ) List 2 14 り 4 っ 0 -4- LO ー 8 0 1 よワ 0 4 ・ 警告ェフ 警告工ラー 警告工ラーにしない予定 * / コンバイラのバグでエラーになる switch 文 List 3 func() switch ( 1 ) { case ' 0 '
保証が得られないことになる。 common initial sequence さて , 前節の冒頭て、「共用体のあるメンバ に対して値を格納し , そのメンバとは型が 異なる別なメンバの値を参照するようなコ ーディングは通常は誤りて、ある」と述べた。 て、は「通常て、はない」ケースて、は「誤りて、はな い」ことがあるのだろうか。 実は , そのようなケースがあることが定 められているのて、ある。その特別の場合と は「 common initial sequence 」と呼ばれてい る。日本語て、は「 ( メンバの ) 共通先頭部並び」 とて、も訳すべきだろうか ( なお余談だが , K& R 第 2 版の邦訳て、は A8.3 て、これを「共通初期 化部分」と訳している。 initial を「初期化」と 訳したために , A8.3 の最後のパラグラフは 何のことかわからない文章になってしまっ た ) 。 List 9 の例を見てもらったほうがわか りやすいだろう。 List 9 て、まず注目すべきは switch 文の制御 式 (controlling expression) のところて、用い ている共用体のタイプタグの参照て、ある。 switch (). sa. tag a) { が , この値が 1 の場合には共用体のメンバ sb ー含まれるメンバ tag a て、ある。ところ こて、参照しているのは共用体のメンバ sa に うテストをする場合には , 原則論からは u が すなわち , 最初の u. sb. tab b 残る。 ングするようにしても , 建て前上の問題は るいは if を使って List 10 のようにコーディ トすることにしても状況に大差はない。あ itch 文を使うかぎり , どのタイプタグをテス 共用体のメンバに属しているのて、ある。 sw のタイプタグは本当に必要な値とは異なる る。つまり switch 文て、テストしている場合 値が 2 の場合には同様に u. sc. c を参照してい に含まれるメンバ b て、ある u. sb. b を参照し , more ある。 わかっていなければならないということて、 現在保持しているメンバは sb て、あることが 代入はおそらく問題ないだろうという予想 うものてある。そう解釈すれば , これらの リに割り付けられる」ことを利用しようとい る構造体て、あったとしても同じ形式て、メモ 体の先頭から並んて、いれば , それらは異な の精神は「同じ型のメンバが同じ順序て構造 しかし , common initial sequence の本来 うのは ANSI 違反なのかもしれない イプタグに代入を行ってから値の代入を行 数 bar や baz のように共通先頭並びの別のタ いのかとも思われる。そうすると List 9 の関 検分 (inspect) て、ある以上 , 代入は許されな 分のどれを検分してもかまわない」て、ある。 ひとつを保持している場合にかぎり共通部 て、共用体オプジェクトが「それらの構造体の こて、 ANSI が規定しているのは , あくま の場合には同一の幅を有する場合 ) 。 している場合 ( およびビットフィールド メンバが整合する (compatible) 型を有 ひとつ以上のメンバに関してそれらの に「共通先頭部並び」を共有する。 なお , ふたつの構造体は次のような場合 検分してもかまわない。 ③複数の構造体に共通の先頭部分のどれを 体のひとつを保持している場合には , ②かっ共用体オプジェクトがそれらの構造 共有する複数の構造体を含んでいて , ①共用体が , それぞれが共通先頭部並びを 定を設けている (ANSI 3.3.2.3 ) 。 すぎるため , ANSIC て、は次のような例外規 しかし , この制約はあまりに杓子定規に ならないという結論が導かれることになる。 用体て、はその外部にタイプタグを置かねば これが真てあれば , List 9 に示したような共 ーディングは誤っていることになる。もし したがって , ここて、示した if 文のようなコ が立つ。 FuII Screen Editor 日本語版 第一 ef プロクラミング・エテイター日卒第新 Full Screen Ed 員 ( 消費税は含ます ) 定価 48 , 000 円 プログラミング・エテイタ ANSI C ー more 121 く資料請求番号 121 〉 TEL. 0425 ー 23 ー 4469 FAX. 0425-27- 引 27 立川事業所ューザ・サポート係 ・お問合せは東京都立川市曙町ト絽ー 2 ー清ビル 日ホシステム槽式会社 新発売 / ムヒ 新機 新機能 新機能 対応機種」一 3m0 、 J -3300 シリーズ価格 19 , 800 円 ( 税別 ) ない人でも OK / 簡単操作の能力開発ソフトウェアです。 格試験等に最適です。「ダイナブック速読」は DOS を知ら で能力がアップします。情報収集、受験、資 読書速度が 3 ~ 5 倍になり、右脳の刺激 ダイナブック速読 ただ今、とっても便利な習′ f マクロ集を公開中 ザ・サポート係までこ・連絡下さい。 方、セット販売希望の方、またサイトライセンス希望の方はユー もいたしております。・お急ぎの方、上記以外のメディア希望の ・英語 DOS では使用できませんが、 B 「 ief 英語版とのセット販売 ・ 5 ・ 2DD 、 3.5 ・ 2DD デュアルメディア・ MS ー DOS3 」以降に対応 東芝、 NEC 、 AX 協議会、旧 M 、富士通、 DOS/V 06 機種対応パッケージ C Users Journa l) アンドゥだ一 - ーおもわず間違うのを期待してしまいそうだ。 (The に不満を覚えたら、これしかない。 ( Oh / FM ) ・まるで夢のような 300 回のアンドウは大きな特徴。 ( 日経バイト ) ・既存のエデイタ ない。 (Jerry Pournelle/BYTE) ・ソフト開発には強力なツール、 ・プログラミング向きェデイタを探している人は、もう探す必要は アメリカでも日本でも高い評価 ョン ) 、メニュー ( オプション ) etc. 数を拡張、リジューム機能、 ctrl キー・アサイン ( オプシ キー操作のファイルへのセープとリプレイ、処理可能行 ・その他の主な新機能 切れても安心です。 容をファイルに自動保存します。だから編集中に電源が Brief は、一定時問、キーを操作しないと最新の編集内 ・安全編集 ます。 ますので、やり直しの面倒を気にせず、安心して編集でき 各ファイルこ・とに 3 圓までの操作を元にもどすことができ ・ 300 回ものアンドゥ 構文を自動生成します。 C 、 Ada 、 Basic 、 COBOL 、 FORTRAN 、 MODULA2 、 Pascal で く構文の自動生成〉 ・テンプレート機能 トやウォッチ・ドッグもセットできます。 ソースコードでデバッグできます。もちろんプレークポイン ・マクロのスクリーン・デパッガ 簡単でカスタマイズも思いのままです。 使い慣れている C 言語でマクロが組めますから、習得が ・ C 言語で組めるマクロ
引 mo Fig. 8 List 7 の LSI C -86 ve 「 3.3 による実行結果 1 .000000 00 00 80 3F 1065353216 1084227584 5 ℃ 00000 00 00 AO 40 9.000000 00 00 10 41 1091 567616 1095761920 1 3 ℃ 00000 00 00 50 41 1099431936 17.000000 00 00 88 41 00 00 A8 41 1 101529088 21 ℃ 00000 1 103626240 25.000000 00 00 C8 41 00 00 E8 41 1 105723392 29 ℃ 00000 33 ℃ 00000 00 00 04 42 1 107558400 もっとも , このため JIS-Pascal の文法の 己述が難解になってしまっているという指 摘があり , 論理的には C 言語のように単純に 入れ子にしたほうがすっきりする。ところ が , 見た目と論理というのは必ずしも一致 してくれないのが世の常て、あって , やはり C 言語のこの書き方はわずらわしく感じられ その問題の使い方とは , 一般にオーバレ そのまま integral (integral は char, すべての C 十十や一部の C 言語処理系て、はこの点が int, および enum の総称 ) として取り出した イ (overlay) と呼ばれるものて、ある。 List 7 ANSI C に対して拡張されている。すなわち 「無名共用体 (anonymous union) 」という機 値」を得たいのて、ある。このような目的には を見てほしい。この例て、は , 共用体の各メ オーバレイというのがひとつの有効な手段 能て、あり , これを用いる場合 , List 6 に示す ンバが重なり合ってメモリ上に割り付けら て、ある。 ように , struct の内側の union には , その un れることを積極的に利用している。すなわ LSI C ー 86 ver 3.3 による List 7 の実行結 ち , float の変数 f と char の配列 b, および 10 ion 自身に対する名前を与えない。そうし 果を Fig. 8 に示すが , 1 行の左端に float の変 ng の変数 n を同じメモリに割り付けること て , この場合のメンバ n のアクセスには , s 数の値が実数形式て、表示され , ついて、それ て、 , f の値をバイト単位てるを通じてアクセス が f00 型のオプジェクトて、あるとすると s. n と が内部的にはどのようなビットパターンと て、きるようにし , さらに f の値を long の整数 書くことがて、き , PascaI と同じになる。 して記憶されているのか , いわゆる内部表 の機能は ANSI C には含まれていないのて、 , としてもアクセスて、きるようにしているの 現形式がバイト単位て、表示される。さらに 残念ながら C 言語を使うかぎりわずらわしく て、ある。 同じビットパターンをそのまま long として ても中間の共用体名を省略することはて、き これはいわば , C 言語の型システムの裏を 解釈したらどのような値になるかが右端に かいているのて、ある。 Pascal て、も同様のこ ただし , マクロを利用して逃れるこ 表示される。 とがて、きる。実際 , C 言語よりもはるかに型 とは可能て、ある。 List 7 の実行結果は明らかに処理系や使 にうるさく , かつ自由なキャストや , 任意 なお , C 十十の無名共用体はここて、示した 用している CPU のアーキテクチャ , あるい の変数のアドレス値を取ってポインタにす 用途以外の意味も持っているのだが , C 十十 は , 場合によっては浮動小数点演算ハード る操作などが許されない標準 PascaI におい に対するこれ以上の深入りは避ける。 ウェアがあるのかどうかなどの環境にも左 ては , オーバレイが型システムの制約を逃れ ーバレイ るための唯一の手段て、あった。通常 , C 言語 右されることになる。 ・・・型システムの裏をかく なお , List 7 の動作説明は , 暗黙のうちに て、 float の変数の値を long として取り出すた float と long のサイズが同じてあることを仮 めに ( long ) て、キャストしたり , long 型の変 前節まて、において , 共用体の基本的な用 数に値を代入したりしても , それは「 float の 定していることを注意しておきたい。 float 途はおのおののメンバが排他的に使用され と long のサイズが異なる環境て、は , このプ 値を ( なるべくその値を保つように ) 型変換 る場合て、あると述べた。共用体のあるメン ログラムの動作は若干変わる。 sizeof(floa して long にした値」が得られるだけて、ある。 バに対して値を格納し , そのメンバとは型 t) >sizeof(long) の場合には float の一部だ 普通はそれが望ましい動作て、あり , 何も が異なる別なメンバの値を参照するような けが long として解釈され , sizeof(float) <s 問題はない。だが , ときとして「内部表現を コーディングは通常は誤りて、ある。ところ 知りたい」というような要求が生まれること izeof(long) の場合には long として解釈する が , あえてそのような使い方を用いるケー メモリ内容の一部には初期化されていない がある。そのような場合にはデータを 1 ビッ スがあることは触れておくべきだろう。 ト単位て、取り出す必要がある。これにはビ あらかじめ注意しておくが , ここて、述べ バイトデータが含まれていることになる。 ット演算を用いることがて、きよう。ところ このように共用体を用いてオーバレイを実 るような共用体の使い方をした場合には , がビット演算の演算子い , & , は integra 現すると , ビットパターンを保ったままて その結果は処理系に強く依存する。移植性 を高くしたいならばこのような使い方は避 1 にしか適用て、きない (ANSI 3.3.10 ~ 3.3. まったく違う型としてアクセスする ( 解釈す る ) 作業をエレガントに記述することがて、き 12 ) 。そのため , 「 float のビットパターンを けるべきてあろう。 1 三ロ ANSI C ー more 119
の位置が格納されます。 値が小さすぎる場合や大きすぎる場合は , errno に ERANGE をセットし , 返り値とし てそれぞれ LONG MIN または LONG MA X を返します。 整数が見つからなければ , *sret に s を格 納し , 返り値として 0 を返します。 strtoul.c unsigned long strtoul( const char *S, char * * sret, int base) ・ long て、なく unsigned long て、値を返すこと 以外 , strtol( ) と同じて、す。 絶対値が大きすぎる場合は , errno に ERA NGE をセットし , 返り値として ULONG N'I AX を返します。 負の整数が入っていた場合 , その整数の 2 の補数を返します (long を単純に unsigned long に変換するだけ ) 。 tolower. C int tolower(int c) 文字 c が 'A ' から ' Z ' の範囲にある場合 , そ れを小文字に変換した値を返します。 それ以外の場合は , c をそのまま返しま す。 通常は , ctype. h て、定義されているマクロ が使われるのて、 , この関数が呼ばれること はありません。 文字列ライプラリ libsrc/c/str の文字列ライプラリて、す (Fig. UNIX て、は , 文字列処理を行う関数が BS D と SystemV とて、異なっています。よって , djgcc のライプラリて、は , 両方の関数が用意 されています。そのため , 同等な機能を持 っ関数が重複して存在します。 また , 同じような関数て、 , 文字列を扱う ものと , 一般の配列を扱うものの両方が用 70 C MAGAZINE 1992 6 Fig. 2 bcmp. c ffs. c 文字列関数一覧 memset. S strcasec. C strncpy. C St 「 e 「「 0 「 . C strsep. C memcmp. C index. C rindex. C strcat. C strspn. C strftime. C strtok. C bcopy. s st 「 chr. C Str 「 Chr. C strncat. C strcspn. C strlen. C strstr. C memcpy ・ s memcpy ・ S memccpy. C strcmp. C strcoll.c strpbrk. c strlwr. c swab. C bzero. S memchr. C strncmp. C strcpy. c strdup. c Strupr. C 意されているものもあります。前者は 0 て、終 了する領域を扱うのに対して , 後者は領域 のバイト数を指定して処理を行うことに注 意してください bcmp. c int bcmp(const void * bl, const void * b2, intlength) ・ バイト数 length の配列 bl および b2 を比較 一致していれば 0 , そうて、なければ 0 以 し , 外の値を返します。 memcmp. C int memcmp (const void * bl, const void *b2, intlength) ・ バイト数 length の配列 bl および b2 を比較 し , 一致していれば 0 , bl > b2 なら正の値 , bl < b2 なら負の値を返します。 したがって , 配列が同じかどうかを判断 するだけなら bcmp() も memcmp() も使え ますが , ソーティングのように大小が重要 なときには memcmp ( ) を使う必要がありま ーされます。 す。領域同士が重なっていても正しくコヒ cnt バイトの配列 src を dst へコヒ。ーしま int cnt) ・ void * dst, void bcopy (const void * src, bcopy. s す。 void * memcpy (void * dst, const VOid * src, int cnt) ・ bc 叩 y( ) と同様て、すが , src と dst の順序 が逆になっています。また , 関数の値とし て常に dst を返します。 bzero. S void bzero (void * s, int l) ・ レヾイトの配列 s を 0 て、埋めます。 ffs. c int ffs(int mask) ・ mask のビットを最下位ビットから順に調 べ , 立っているもっとも低いビットのビッ ト番号を返します。どのビットも立ってい なければ 32 を返します。 ffs という名前は , 同じ機能を持っ VAX の 命令 (find first bit set) から来ています。 この関数は , OS のカーネルの中て、実行可 能なプロセスを探すときなどに使われます。 index. C char *index(const char *p, int (h) ; 文字列の p の中て、最初に現れた文字 ch への ポインタを返します。見つからなかったと きは NULL を返します。 strchr. c char *strchr(const char *p, int (h) ・ index ( ) とまったく同じて、す。
リが確保される , という ことがありません。 これが , とても重要なことて、す。 static なメン / ヾは , そのクラスの外て、実コ ードとしての定義と初期化が必要て、す〔ク ラス定義 ( といラよりもクラスの宣言 ) の 中に書かれているだけて、はメモリ上に実在 化しない〕。このように , 定義を別途書くこ とによってメモリが確保されますから , 定 義がどこにもないとリンカが未定義シンボ ルエラーを出します。すなわち , 定義によ って static なメンノヾのためのメモリが ( たっ たーっの場所に ) 確保され , main() が始ま る前にそのメンノヾのためのコンストラクタ がコールされます〔その static なメンノヾがユ ーザ定義型て、あった場合〕。 また , main ( ) が終了する前にはデストラ クタがコールされます〔その static なメンバ がユーザ定義型て、あった場合〕。 static なメ ンバを定義し初期化するときは , ERRTES T. CPP の中にもあるように , 必ず、 いうスコープ指定演算子を書いてください : demonstration ( ) の中て、は , term etest : inate() をちょうど printf ( ) と同じゃり方て、 コールしています。こういう簡単な使い方 が , この error handler クラスを作ったねら いて、す。 iostream ふうの方法 ェラーメッセージを printf( ) ふうに表示す るやり方は , クラスのユーザが C 十十になじ みが薄いときには , とくに有効て、す。しか し , printf ( ) 的なやり方には , 可変引数リス トの扱いに関して幾つかの間題があります。 〃は単に、、引数の個数が不定〃 という意味なのて、 , これに関して渡される 引数に関しては , コンパイラは〔関数プロ トタイプに照らしての〕データ型のチェッ クがて、きません。さらに , 書式指定文字列 を正しく書かなかったり , 逆にリスト中の 引数の個数が書式指定文字列の指定より少 ないときには , ごみがプリントされてしま うて、しよう。どちらの場合も , クという / ; 10St 「 eam を使った書き方 cout くく” hello, world, l'm ”くく 5 くく” on ' くく ctime(&now) くく endl; 8 : 9 : 10 : 12 : 13 : 14 : 15 : 16 : 17 : 20 : 22 : 23 : 24 : 26 : 29 : 30 : List 5 IOSERROR. H iostre 囲を使う簡単なエラーハンドラ . 1 : / / : IOSERROR. H ー 2 : / /. 他のクラスやプログラムの中にこのクラスの static な 3 : / /. オブジェクトをインストールすることによってエラー 4 : / /. 処理の基本枠組みが設定されるので , 後でもっと高度 5 : / /. なものに変更して再コンパイルすることもできる . 6 : #ifndef IOSERROR_H_ 7 : #define IOSERROR_H #include く iostream. h 〉 #include く strstream. h> 11 : 〃このマニピュレータは出力ストリームに改行を挿入する : inline ostream& nl(ostream& (S) ( return OS くく” \ Ⅱ” ; ) class ioserror : publ ic ostrstream { 〃このクラスの”ローカルな const ” enum { bsize = 300 } ; char msgCbs ize] : 〃ェラーメッセーシ・を入れるためのハ・ツファ char* classname; 〃このハント・ラを使うクラスの名前 18 : public: ioserror(char * class—name) ; 〃クラスの名前 void dump() { if(*msg) { 〃メッセージがあればダンプする 27 : 〃このマニピュレータはエラーメッセージをすべてプリントして clog くく nl くく classname くく " error: " くく msg くく endl; 31 : #endif / / IOSERROR_H_ ostream& terminate(ostream& (S) ; 28 : / / プログラムを終了させる : 意図的に曖昧な指定によって , コンパイラ にはチェックの道が閉ざされています。引 数リストを正しく書くことが , もつばらプ ログラマの責任になってきます。 printf ( ) の こういう問題点だけて、も十分に厄介て、すが , わけの分からないエラーメッセージを読ま される側は , なおさら困ります。 この , I / O て、データ型チェックが行われな いという問題点が , C 十十の標準ライプラリ として iostream が作られた理由の一つて、 す oiostream を使うと ,List 4 のような書き 方がて、きます。 cout というオプジェクトは , C の標準出力 stdout とよく似たものて、 , < < という演算子 は , ストリームオプジェクトに対して使っ たときには、、出力せよ〃という意味になるよ う , オーバロードされています。 < < 演算子 の間の式は , ーっひとっ別々に評価されま す 0List 4 の実行文の中て、は , 実にいろんな ことが行なわれます。文字列が二つあるし , 文字列を返す ctime ( ) という (ANSIC の ) 関 数コールがある。そして整数 ( 5 ) があり , マ ニピュレータと呼ばれるものの一種てある endl があります。マニヒ。ュレータとは , 単な る出力て、はなく , 何か別の処理を伴ってス トリームに出力するもののことて、す。この endl というマニヒ。ュレータは , ストリームに 改行を出力してから , そのストリームをフ ラッシュします〔訳注 : Borland C 十十 2. 0 の日本語マニュアルは , マニヒ。ュレータ (manipulator) に、、処理子〃の訳語を与えてい ます (PROGRAMMER' S GUIDE, pp. 22 引数が , 文字列か , int か , float か , 等々 に応じて , それぞれ違う関数がコールされ ます。そういう内蔵データ型だけてなく , 自分が新たに作るクラスの中に独自の iostr eam 関数を定義して , そのクラスのオプジェ C 十十でエラー表示を楽にやろう 17
を返すような処理になる ( もちろんほかの形 式て、情報を返す手法もある。これは一例て、 しかない ) 。 この際 , その値が long 型て、あるのか doub le 型て、あるのか , あるいはそのほかの型の値 て、あるのかというのはお互いに排他て、ある 点に注意したい。すなわち , ある値の「型」 は必ずひとつだけて、あり , long 型て、あると 同時に double 型て、もあるなどという状況は あり得ない。したがって , このようなデー タを表現するのには , あらかじめ定められ た複数の型のうちのひとつを排他的に選択 して , その型の値を記憶することがて、きれ ば十分て、ある。 このように「お互いに排他」な型の値 , す なわち「ある瞬間には異なる複数の型のう ち , どれかひとつだけが必要て、あり , 同時 にふたつ以上が要求されることはない」とい う状況下て、 , これらの値を一括してひとつ の変数に記憶しておきたいという状況は実 際しばしば起きる。 ほかの例としては , 「 ( 1 台の ) 車」を表す 変数を考えよう。車の情報を詳しく管理し たいのだが , 実際には四輪普通乗用車と四 輪軽乗用車 , 自動二輪および原付などによ って管理すべき項目というのは大幅に変化 する。そのため , これらはやはりそれぞれ 別の型としてあらかじめ定義しておき , 実 際の車種に合わせて型を変えて記憶するの が妥当て、ある。この場合も , 1 台の車が同時 に四輪車てありかっ二輪車てあるということ は ( 超合金変形マシンて、もないかぎり ないだろう。すわなちそれらの型はひとつ の変数に関しては排他的に使用されること がわかる。 以上のように , メンバがお互いに重なり あってメモリ上に割り付けられていても , それらが排他的な形て、のみ使用されるなら ば , 記憶内容が不用意に破壊されるという 状況は起きず , そのようなケースては共用 体を用いても問題は生じないことがわかる。 ところて , そのようなケースに構造体を 用いることはて、きないのかという疑問が生 じるだろう。 結論からいえば , 別に構造体て、も問題は とくに共用体て、なければならない理 由というのはない。共用体が有利なのは , メモリ使用効率て、ある。構造体を利用した 場合には , 現在使われているメンバのほか のメンバには有効な値が設定されないまま , メモリがまったくムダになってしまう。 れに対して共用体て、はメンバは極力メモリ を共有するように割り当てられるため , ム ダを最小限に抑えることがてきる。 ただし , このケースて、はムダといっても , たかが数バイトてある。ほとんどの場合こ れは誤差の範囲て、あり , この節約を理由に 共用体の使用を正当化するのは難しいだろ う。あえて理由をあげればプログラマの「気 持ち」がすっきりするという程度だろうか。 共用体によるメモリ使用効率の向上が明 白な意味を持つのは , 構造体 , あるいは共 用体を多数個記憶する必要が生じた場合て、 ある。端的な例としてはデータベースて、あ る。全体て、 10 万件のデータベースがあった として , その 1 レコードにつき 10 バイト省略 て、きたとしたら , 全体て、 IM バイトの節約に なる。もし全体の件数が 100 万件になれば , 差は 10M バイトに達する。これは決して無 視て、きる違いて、はないだろう。 構造体との併用 , ・タイプタグ 共用体の変数に対して , 現在代入されて いるのがどの型の値て、あったかを管理する のはプログラマの責任て、ある。これがあや ふやになってしまうと正しい値を参照する ことがて、きない。このため , 共用体は構造 体の中に入れ子の形にして用いられる場合 が多い。外側の構造体のメンバに「現在どの 型のデータが共用体に記憶されているか」を 記憶しておくわけて、ある。具体例として Li sp 系言語のデータ構造を用いて解説してみ よう。 more Lisp 言語て、は S 式と呼ばれる二進木をデー タ構造の基本にしている。 Lisp の伝統的な インプリメンテーシゴンて、は , 二進木のノ ードはセルと呼び , car と cdr というふたっ の子供 ( サブツリー ) へのポインタを有して いるだけて、ある。そして葉に当たる部分に は通常アトム (atom) と呼ばれるデータがや ってくる 0Fig. 7 にその例を示すが , 図中の A, B, および NIL というのがアトムて、あ る。それ以外の四角い箱がセルて、ある。 以前の Lisp て、はアトムを表現するのには 特別な工夫をして , car, cdr のフィールド を有するセルだけて、済ませていた。 ところが最近のインプリメンテーション て、はときと場合に応じてノードの内部構造 を変えて , その中に値を直接保持するのが 普通て、ある。そして , 同じノードがときと してセルにもなり , アトムにもなるように している ( アトムは葉に相当する部分にしか 現れないから , 本来はノードと呼んて、はい けないのだが , 行きがかり上そのように呼 ぶ ) 。しかも , Lisp て、はとくに型宣言をしな いかぎりアトムはどのような型の値て、も保 持て、きる。 このためアトムの中には整数や実数 , 文 字列やリストなど各種の型の値を記憶する ためのメンバを宣言する必要がある。しか も , セルというのは非常にたくさん使われ るものて、あるし , 通常主記憶上に存在する ため , やはり少して、もメモリを節約したい このためノードの定義には共用体を使うの が普通て、ある。 List 2 にフリーソフトウェアとして配布さ れている David MichaeI Betz 作の XLISP 2.1 の typedef によるセル定義をあげておく。 比較的大きな構造体 / 共用体の宣言て、ある が , その骨格は List 3 のようになっている。 すなわち , 外側に structnode という構造 体の宣言があり , そのメンバは char n tYP e, char n flags, および uni ninf0 n inf0 こて、先頭のメンノヾの char の三つてある。 n_type というのが実際に union ninf0 n inf ANSI C ー more 115
ーゲット CPU て、生成て、きるようになりまし 位置独立コードとは , どのようなアドレ スに配置されても正しく動作するコードの ことて、す。このためには , コード内部て、の 分岐命令を絶対分岐て、はなく相対分岐にす る必要があるだけて、なく , 一般のデータの アドレスもプログラムカウンタからの相対 値て、指定しなければなりません。したがっ て , 位置独立て、ないコードと比べて , コー ドサイズが増大し , 実行速度も低下してし まいます。 位置独立なコードは次のような場合に使 われます。 まず , マルチプロセッシングを行う場合 , 複数のプロセスて、メモリを分けあわなけれ ばならないのて、 , コードの物理的なアドレ スは , コンパイル時 ( およびリンク時 ) には 決められません。 このような場合には , 次のいすれかの方 法をとる必要があります。 ①コードを位置独立にする ( Mac , OS ー 9 な ど ) ②実行時にコードをロードしたときに実際 のアドレスを決める (MS-DOS の EXE ファ イルなど ) ③各プロセスに独立な論理アドレスを割り 当てる ( 仮想記憶に対応した多くの OS ) また , 共有ライプラリのコードも位置独 立て、あることが要求されます。共有ライプ ラリとは , 複数のプログラムから同じライ プラリが呼ばれているときに , そのライプ ラリの実体をひとつだけメモリ上に置き , それらのプログラムの間て、共有する , とい 、 0 うものて、す。 26 C MAGAZINE 1992 6 ます。 ードのディスク上て、のサイズとが節約て、き これによって , 実メモリとオプジェクトコ なかったら ) ファイルから読み込まれます。 のライプラリがメモリ上に読み込まれてい ンクされず , プログラムの起動時に ( もしそ ライプラリはオプジェクトコードにはリ 仮想記憶機構を持っ OS て、この共有ライプ ラリを実現すると , 異なる論理空間を持っ 複数のプログラムから同じライプラリが呼 ばれるため , ライプラリのコードは位置独 立て、ある必要があります。 GCCVer. 2 て、位置独立なコードを生成さ せるには , コンパイラの起動時に一 fpic また は -fPIC オプションをつけます。このうち , ー fpic はグローノヾルオフセットテープルのサイ ズをオーバすると使えないのて、 , その際に は一 fPIC を使います。 Ver. 2 て、位置独立コードに対応している CPU は 68000 , 88000 , 80386 , SPARC, RS/ 最適化の改良 6000 て、す。 ことがわかったとき , その変数を定数て、置 「定数伝播」とは , ある変数が定数て、ある 大域的な定数伝播 法のいくっかを紹介します。 こて、は , Ver. 2 て、追加された最適化手 実行速度がさらに改善されています。 Ver. 2 て、は , 最適化機能の向上によって , ありました。 コンパイラのコードが速くなることがよく ARC チップを採用した Sun ー 4 て、は , Sun の C いコードが出ることが多いのに対して , SP は , Sun の C コンパイラより GCC のほうが速 ていました。実際 , 680X0 を使った Sun ー 3 て、 は , これらの CPU て、の性能が相対的に劣っ されています。そのため , 以前の GCC て、 た局所的な最適化を行うことを前提に設計 般的な最適化だけて、はなく , CPU に依存し ーキテクチャて、は , このような古典的・ かし , 最近の RISC などのコンパイラ指向ア し 的な最適化に強いといわれてきました。 GCC は , 以前から機種に依存しない一般 というコードがあり , 「・・・・・・」て、示した箇所 て、変数 a ~ の代入が行われていなければ , 関 数呼び出し i ( a ) は i ( 1) て、置き換えることがて、 きます。この置き換えは定数伝播と呼ばれ , 変数としてしか扱えなかった式に対して , 定数のみに適用て、きる最適化が行えるよう になる , という効果があります。 たとえば , 定数式の畳み込み ( 2 * 3 を 6 て、 置き換える ) と無用命令の削除 ( 使われない データを計算しているコードなどを取り除 く ) を組み合わせると , int a if (a * 2 else 2 ) というコ int a ードを , き換える , int a というものて、す。たとえば , に簡約化することさえて、きます。 このように , 定数伝播に基づく最適化は 大きな効果を生じることがありますが , GC C Ver. 1 て、は , 定数伝播解析が , 原則とし て基本プロック内て、しか行われていません て、した。したがって , 上記の置き換えは行 われていても , 変数への代入と参照とのあ いだて、基本プロックが分断された場合 , っ まり , 次のような場合は , この最適化は行 われませんて、した。 int a if (a * 2 else これて、は , 定数伝播による最適化がかな り制限されてしまいます。そのため ver. 2 て、はこれが改善され , ある程度大域的な定 数伝播解析が行われるようになりました ( た とえば上のようなコードて、も最適化が行わ れます ) 。