int - みる会図書館


検索対象: 月刊 C MAGAZINE 1990年7月号
64件見つかりました。

1. 月刊 C MAGAZINE 1990年7月号

愛 n 0 「 Error Ha れ引 ing D O S 〃 , p p . 13 0 ー 13 3 , M i c r 0 s 0 f t List 2 226 : 227 : 228 : 229 : 230 : 231 : 232 : 233 : 234 : 235 : 236 : 237 : mov push mov iret END TEXT ends CB-trap ax, endp word word ptr cs : Ca Ⅱ erAddr 工ラーコ ptr cs : ErrorCode , アプリケー ドを ax に ションに戻る ライプラリはエラーを正しく扱うのて、 , C 「 itica 旧「「 0 「は必要ありません。このシステ ムを , ほかのコンパイラを使用するプログ ラムの中て、使用するときは , そのコンパイ ラのライプラリを最初に調べてください MASM て、 ce. asm(List2) をアセンプルする には MASM / ML CE, また Turbo C て、 demo. c(ListI) をコンパイルしリンクするに は TCC DEMO CE. OBJ とします。 このシステムを使うと , 致命的工ラーを List 3 インタフェイス関数の使用例 / * 元の関数を復帰 setCEasker( oldasker ) : / * ・・・プリンタをここで使用する / * 別のインタフェイス関数をインストールして・・・ * / setCEasker( printer-asker ) : / * 現在のインタフェイス関数をセープ * / oldasker getCEasker() : askerfcnptr oldasker; 自作ユーサインタフェイスのプロトタイプ int far asker(int axreg, ind direg, int bpreg, List 4 アプリケーションにとって透明化すること がて、きます。これによってメンテナンスが 容易になり , 可搬性と信頼性を高めます。 [ 参考文献 ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] Ray Duncan, Press, 1986 、、 Advanced MS ー List 5 28 : 26 : 25 : 24 : 23 : 22 : 20 : 19 : 17 : 16 : 15 : 14 : 13 : 12 : 11 : 9 : 6 : 4 : 2 : ce. h 1 : / * ce. h 一致命的工ラーと a sker 関数のデータ型 * / 3 : #define CE_INTERRUPT 0X24 5 : void interrupt CE_trap(void) ; 7 : typedef int (far *askerfcnptr) (int, 8 : void far setCEasker(askerfcnptr) : askerfcnptr far getCEasker (void) : int sireg); int) : / * 以下はエラーコードの意味 ... * / l=write * / int, int, WiIIiam James Hunt, 、、 The C T001b0X 〃 , pp. 370 ー 377 Addison ー WesIey , 1985 、、 DOS 2 . 0 Technical Reference ′′ pp. D-4 ー D ー 8 , IBM , 1983 Peter Norton, 、、 Programmer s Guide to the IBM ー PC" ,pp. 157 ー 160 Microsoft Press, 1985 Dan RoIlins , 'DOS Exception Han 、、 P C T e c h J 0 u r n a 1 d I i n g , Apr. 1987 , pp. 130 ー 140 typedef un ion { uns igned short ax : struct { unsigned drive : 8 : unsigned r_W : 1 : unsigned area unsigned actions unsigned unsigned typ } parts : 21 : } AXBITS; typedef union { O=read : / * 0=disk error; l=non-disk / * O=DOS sys; I=FAT; 2=dir; 3=data * / Danny Lawrence 氏はもとテネシー州の Kingsport Times-News 社のプログラマ て、 , 現在は MIDI アプリケーションのソフト ウェアを開発しています。 uns igned short di : struct { } parts; unsigned char di-hi : uns igned char errorcode : 28 29 : } DIBITS; C M AGAZIN 1990 7

2. 月刊 C MAGAZINE 1990年7月号

List1 GCC にインプリメントてきましたのて , 例 として紹介してみます。 まず , remote をキーワードとして , パー サに認識させる作業から開始します。「 emote は記憶クラスて、すから , いちおう文法上は s ね tic や extern と同じ位置に属することとし ます。字句解析 y ⅵ ex ( ) が extern を認識した とき構文解析 yyparse( ) に返す値をソースか ら調べ , それと同じことを remote について 行うように改造すれば文法上問題なくなり ます。 List1 は c parse tab. c ( これは bison の出 カファイルてす ) の字句解析初期化部分とキ ーワード探索ルーチンてす。コメントをご このキーワード 覧になるとわかるように 探索ルーチンは人間の手て、生成するのて、は なく , GNUi•イストリビュートの中に含ま れる g 十十て、記述された gpe 「 f というツール て、生成します。具体的にはオリジナルのパ ーサからキーワードの部分を抽出して , 、、「 emote クを追加し再度 gpe 「 f て、このルーチ ンを生成した後 , それを . y ファイルに組み 構文木の変数に D - REMOTE を追加する 124 : 125 : 126 : 127 : 128 : 129 : 130 : 131 : } 132 : 133 : / * The elements of ridpointers' are identifier nodes for the reserved type names and storage C lasses. is indexed by a RID 135 : value. 136 : 137 : tree ridpointers[(int) RID-MAX] : 138 : 139 : vo i d 140 : init_lex ( ) 141 / * Start it at 0. because check_newl ine is called at the very beginning 142 : 本 / 143 : and wi Ⅱ increment it tO 1. 144 : ⅱ neno = 0 : 145 : 146 : = 40 : maxtoken (char * ) xnalloc (maxtoken + 2 ) , 147 : token_buffer - 148 : max_wide = 40 : (int * ) xmalloc (max_wide + l); 149 : wide_buffer 150 : ridpointers [(int) D 」 NT] ー get-identifier ( ” int つ : 151 : ridpointers [(int) RID-CHAR] ー get-identifier ("char") : 152 : ニ get-identifier ("void") : ridpointers[(int) RID-VOID] 153 : ridpointers [(int) RID-F し OAT] : get-identifier ( " oat つ : 154 : ridpointers [ (int) RID-DOUBLE] = get-identifier ("double ” ) : 155 : ridpointers [(int) RID-SHORT] = get-identif ier ("short ” ) : 156 : ridpointers [(int) RID-LONG] get-identifier ( コ ong つ : 157 : ridpointers [(int) RID-UNSIGNED] = get_identifier ・ ("uns igned") : 158 : 第 get-identifier ("signed"); ridpointers [(int) RID-SIGNED] 159 : ridpointers[(int) RID-IN い NE] = get-identifier ( ” inline ” ) : 160 : ridpointers [(int) RID-CONST] = get-identif ier ("const つ : 161 : ridpointers [(int) RID-VO し ATILE] = get-identifier ( ” volatile つ : 162 : get-identifier ("auto") : ridpointers [(int) RID-AUTO] 163 : ridpointers [(int) RID-STATIC] = get_identifier ("static つ : ridpointers[(int) RID-EXTERN] = get-identifier ("extern") : 165 : ridpointers[(int) RID-TYPEDEF] = get-identifier ("typedef") : 第 166 : ridpointers[(int) RID-REGISTER] = get-identifier ("register") : 167 : = get_identifier ("renote つ : ridpointers[(int) RID-REMOTE] 168 : 169 : } register char *S ニ wordlist[key]. nane; = *str & & ! strcmp (str 十 1. s + 1 ) ) if (*s return &wordlist[key] : を return ( 0 ) : 構文木のフィールドに「 emote アトリビュートを追加する List LiSt 1 ー【じ , Cx2 Z ↑ー 00 E—↑ー Z ー・ 00 , 00 tree. h- See the accessor macros, defined below. for documentation Of the fields. tree. h- tree. h- tree. h—struct tree_common tree. h- { tree. h- i nt u i d : 第 union tree node *chain ; tree. h- tree. ト union tree node *type; unsigned char COde : 8 : tree. ト tree. ト tree. h- unsigned externa l-attr tree. h- unsigned public-attr tree. h- unsigned static-attr tree. h- unsi gned volatile attr tree. h- unsigned packed-attr unsigned readonly_attr tree. h- tree. h- unsigned literal-attr unsigned nonlocal—attr tree. h- tree. h- unsigned permanent_attr tree. h- unsigned addressable attr tree. h- unsigned regdecl -attr unsigned this vol-attr tree. ト unsigned unsigned-attr tree. h- unsigned asm-written attr: 1 : tree. h- 三 tree. h- unsigned inline attr tree. h- unsigned used-attr unsigned lang-flag-l tree. h- 第 uns i gned lang-f ね g ー 2 tree. h- unsigned ng ゴ g -3 tree. h- unsigned Iang-flag-4 tree. h- / * OSK added 本 / unsigned remote-attr : 1 tree. h : / * There is r00 田 for three more attributes. * / tree. h- tree. ト } : tree. ト tree. ト / * Define accessors for the fields that all tree nodes have tree. ト (though so 記 fields are not used for all kinds 0f nodes). tree. ト tree. h-/* The unique id Of a tree node distinguishes it fron all Other nodes 」 ( 」いいいリりリリりをい、新 136 CMAGAZINE 19 7

3. 月刊 C MAGAZINE 1990年7月号

CC 」を参照していただくことにして , 本記事 GN び奮闘記ー を読んていただくために必要な点を少し説 明しておきましよう。 簡単な GNU C の コンパイラの構造は , 本誌て、何度も取り 上げられていますし , これだけてひとつの 改造方法 1 本がてきてしまうほど奥深いものてす。詳 しい説明は専門書を読んていただくこ して ,GNU C て、の構成を簡単に述べましょ g 十十移植記でも述べましたが , X68000 では and Porting GNU CC 」に詳しく書かれて う。 GNU C はおおよそ Tbl. 1 のような構成 GNU C が稼働しています。 GNIJ C はクロ います。が , 英文てすし , これまた私自身 成になっています。 スコンパイラの性格が強いコンパイラです。 がすべてを理解しているわけてはないてす もちろんこれらは互いに関連して動作し ソースも入手できるので , その気になれば が , 簡単なピープホール的改造なら , GCC ます。 g 十十移植記の際にててきましたが , かなりおもしろい改造を加えて独自の機能 のソース自体と , このドキュメントてなん GCC はターゲットマシンコードを直接生成 を拡張できます。そこで , Ver. 1 . 37.1 のソ とかてきるようて、す。そこて , 詳しい技術 するのて、はなく , RTL コードを生成し , そ ースをもとに , 改造手順を少し紹介してみ 的な内容はこの「 Using and Porting GNU れをターゲットマシンコードに変換します。 ます。ただ , 私自身がコンパイラのプロで TbI. 1 GNU C の構成 あるわけではなく , バグの少ない GCC に多 くのエンバグを加えることにもなりかねな いので , そこは「無保証」ということでご容 赦願います。 基知識 GCC の移植は , GNU C 添付の「 Using Fig. 1 0S9 での「 emote 記憶クラスの拡張 ( MWC ) 例 「 emote int data; foo( ) 第 4 回 吉野智興 該当ソースファイル ( UN Ⅸでは凵は・一 , です ) c-parse. y, c-decl. c, c-typeck. 字甸解析 , 構文解析 コード生成 最適化 exp 「 . c,stmt. c, optabs. c.. ー 00P. C , cse. C, combine. C.. キーワード検索八ッシュの変更 ( c - pa 「 se - tab. c より ) List 1 : / * C code produced by gperf version 1.5 * / 2 : / * Comnand-l ine: p:ygperf. x -0 -a -g -t -N is-reserved-word -jl -kl' 3 , 4 * / 3 : 4 : 5 : / * This is an example of the source include faci lity. 6 : Everything between the delimiters is included in the generated output file verbatim. * / 7 : 8 : 9 : struct resword { char *name; short tOken; enum rid rid; } : 11 : #define MIN_WORD_LENGTH 2 12 : #define MAX_WORD_ し ENGTH 13 13 : #define MIN_HASH_VA し UE 2 14 : #define MAX_HASH_VA し UE 81 16 : 52 keywords 17 : 80 is the maxinum key range 20 : inline static int hash (register char *Str. register int len) { static const int cval[] 21 : 81. 81 , 22 : 23 : 24 : 25 : 26 : 29 : 81. 30 : 81, 8 し 34 , 31 : 0. 81 , 6 , 32 : 10 , 32 , 7 , 0. 81, data=10; . data は remote クラス move.l #data, dO (a6,dO.l),aO : 32 ビットオフセットでアドレスを得る lea move.l # 1 0 , (aO) : テータを代入 Fig. 2 木構造に「 emote 属性を登録 ( 1 ) TREE_EXTERNAL (decl) = ! initialized & & (specbits & (l<<(int) RID-EXTERN)); TREE-REMOTE (decl) = ! initialized & & (specbits & (l<<(int) RID-REMOTE)); Fig. 3 木構造に「 emote 属性を登録 ( 2 ) TREE-REMOTE (decl) =specbits & (l<<(int) RID-REMOTE); 134 CMAGAZINE 19 7

4. 月刊 C MAGAZINE 1990年7月号

ン■イ int tm hour ・ int tm mday int tm mon ・ int tm_year ・ int tm wday i nt tm_yday int tm isdst ・ なし 時 ( 0 ー 23 ) 日 ( ト 31 ) 月 ( 0 ー 11 ) 1900 年からの年数 日曜日からの日数 ( 0 ー 6 ) 1 月 1 日からの日数 ( 0 ー 365 ) 夏時間設定用のフラグ ・エラー * 注意 GMT 値は , グリニッジ標準時 1970 年 1 月 1 日 0 時 0 分 0 秒から経過した秒数が格 納されている。 1980 年 1 月 1 日以前の場合には loca は ime 関数は NULL を返す CMS-C Ve 「 . 5.1 ] ・用ラ去 time t mktime( struct tm * timeptr ) く time. h 〉 #include ■機能要約地方時間を GMT 値に変換する mktime() ・引数 struct tm { int tm sec int tm min ・ int tm hour ・ int tm mday i nt tm mon ・ int tm_year ・ int tm wday int tm_yday ; int tm isdst ・ 時間 / 日付の構造体 秒 ( 0 ー 59 ) 分 ( 0 ー 59 ) 時 ( 0 ー 23 ) 日 ( 0 ー 31 ) 月 ( ト ll) 1900 年からの年数 日曜日からの日数 ( 0 ー 6 ) 1 月 1 日からの日数 ( 0-365 ) 夏時間用のフラグ stime() [TurboC Ve 「 . 2.0 ] ー機能要約現在の時刻を設定する #include く time . h 〉 ・用法 int stime( time t *tp ) ・ ■引数 GMT からの経過秒数 ■戻り値成功した場合は 0 を返す time() ■機能要約 GMT からの経過を秒数で返す #include く time. h> ・用ラ去 time t time( time t *timeptr ) ・引数時間を格納する場所 ■戻り値経過した秒数 ■工ラー N ULL tzset( ) ー機能要約 3 つのグローバル変数に各値を格納する #include く time . h 〉 ・用法 void tzset( void ) ・ ・戻り値なし ■工ラーなし * 注意以下のグローバル変数に格納される } *timeptr tm 構造体へのポインタ CMS—C Ve 「 . 5.1 ] * 注意バッフアは少なくとも 8 バイトと終端レヾイトが必要 ■引数日付を格納するバッファ ■用法 char * strdate( char * date ) く time . h 〉 #include ■機能要約現在の日付を文字列に変換する —strdate() * 注意引数が 1980 年 1 月 1 日以前の場合には , mktime ( ) 関数は一 1 を返す ・戻り値変換された GMT 値 [MS-C Ve 「 . 5.1 ] * 注意バッフアは最低 8 バイトと終端 1 バイトが必要 ・エラーなし ・戻り値 char * : time へのポインタ ( 時間の形式は hh : mrn :ss) ■引数 char *time : 時刻を表す文字列 ・用ラ去 char * strtime( char *time ) く time. h 〉 #include ■機能要約現在の時刻を文字列に変換する —strtime() timezone daylight tznameC0] tzname[ 1 ] utime() GMT との時差 ( 秒 ) TZ 設定において夏時間調整ゾーンを指定したときには 0 以 外 TZ 設定からの 3 文字の文字列の値 夏時間調整の文字列。 TZ 設定が夏時間調整ゾーンを省略の 場合はから文字列となる CMS-C Ve 「 . 5.1 ] ■機能要約書き込み可能なファイルの変更日時をセット #include く sys%types. h 〉 く sys%utime. h 〉 ・用ラ去 int utime( char * path, struct utimbuf * times ) ■引数ファイルのバスネーム 格納された時間値を示すポインタ struct utimbuf { time t actime : アクセス時間 time t m ; 変史時間 } *times ・ ■戻り値正常終了 0 ■工ラー ー 1 , e 「「 no に次の値をセット EACCES OMFILE ENOENT EINVAL バス名がディレクトリまたは読み込み専用ファイル オープンしているファイルの数が多すぎる ファイルまたはパスが見つからない 引数が正しくない C プログラマのためのランタイムライプラリ入門 門 83

5. 月刊 C MAGAZINE 1990年7月号

ランタイムライプラリー覧表 4 ランタイムライプラリー覧表 4 asctime ( ) ・機能要約日付および時間を ASC 文字列に変換する #include く time. h 〉 ・用ラ去 char *asctime( time ) struct tm * ti me ■引数 tm 構造体の内容は以下のとおりである int tm m day i nt tm wday ー 1 const struct tm int tm int tm int tm i nt tm i nt tm sec mln hour ・ mon year ・ 秒 ( 0 ー 59 ) 分 ( 0 ー 59 ) 時 ( 0 ー 23 ) 日 ( 1 ー 31 ) 月 ( 0 ー 11 ) 1900 年からの年数 日曜日からの日数 ( 0 ー 6 ) 1 月 1 日からの日数 ( 0 ー 365 ) 夏時間設定用のフラグ #include く time. h 〉 ・用法 double difftime( time t time2, time t timel ) ・戻り値 timel から time2 までの経過時間 ■工ラーなし * 注意 MS-C ve 「 . 5.1 では , 実行結果の表示に doub 厄のキャストを行う必要がある ftime() ■機能要約現在の時間を構造体にストアする #include く sys%types. h 〉 く sys%timeb. h 〉 ・用ラ去 void ftime( struct timeb * timeptr ) i nt tm_yday i nt tm isdst ・ ー引数 struct timeb { millitim ・ ti mezone dstfl ag GMT ( 1970 年 1 月 1 日 0 時 0 分 0 秒 ) から経過した秒数 1 秒以下をミリ秒で格納 地方時間と GMT との差を , 西回りの分単位で表現 夏時間調整が地方時に有効な場合は 0 以外の値 } tm *time ・ asctime ( ) 関数が返す文字列は tm 構造体に収められている , 値を文字列にす Mon Jan 02 02 : 03 : 55 1980 \ n \ 0 にその参考書式を示す * 注意 asctime によって得られる文字列は長さが 26 文字で , 24 時間表示である。以下 なし ・エラー ・戻り値変換された文字列への先頭アドレス clock() る ・機能要約この関数を起動したプロセスの実行に要したプロセッサ時間を得る } * timeptr ー戻り値なし ■工ラーなし * 注意 timezone フィールドは , グローバル変数 timezone から算出され , dstflg フィー ルドは , グローバル変数 day ⅱ ght から算出される gmtime() ■機能要約指定した時刻を tm 構造体に変換する #include く time. h> ・用ラ去 struct tm * gmtime( const time t * time ) ■引数時刻を格納した先頭アドレスへのポインタ #include く time. h> ・用法 clock t clock( void ) ・ ・戻り値プロセッサの秒数 ・エラー計測したプロセッサ時間が無効であるとき ctime() ・機能要約 time t 型の時刻を文字列に変換する #include く time . h 〉 ■用ラ去 char * ctime( const time t * time ) ■引数時刻を保持するバッフアへのポインタ ■戻り値変換された文字列の先頭ポインタ ・エラー 1980 年以前の日付が指定されていたときは , ・戻り値 struct tm* { int tm sec ・ int tm min : int tm hour ・ int tm mday int tm mon int tm_year ・ int tm wday int tm_yday int tm isdst ・ ーエラーなし 秒 ( 0 ー 59 ) 分 ( 0 ー 59 ) 時 ( 0 ー 23 ) 日 ( 1 ー 31 ) 月 ( 0 ー 11 ) 1900 年からの年数 日曜日からの日数 ( 0 ー 6 ) 1 月 1 日からの日数 ( 0 ー 365 ) 夏時間設定用のフラグ NULL が返る * 注意 t 面 e 関数で得られた time t 型の時問を以下のような 26 個の文字列 る。 asctime ( ) 関数とは異なっている。関数どうしの関係図を参照 Tue May 01 12 : 12 : 12 1990 \ n \ 0 difftime() ・機能要約時問の差を計算する こ変換す * 注意引数 time に 1980 年 1 月 1 日以前を指定した場合は NULL を返す localtime() ・機能要約 GMT から地方時問に直して tm 構造体に格納する #include く time. h> ■用ラ去 struct tm * localtime( const time t * time ) ・引数 GMT 値へのポインタ ■戻り値 struct tm* { int tm sec ・ int tm min ・ 秒 ( 0 ー 59 ) 分 ( 0-59 ) 82 CMAGAZINE 19 7

6. 月刊 C MAGAZINE 1990年7月号

•const const を指定することにより , 読み出し専 用として限定することがて、きます。このタ イプのオプジェクトの値は , コンパイル時 に設定 ( 初期化 ) されており , その値を変更 するような操作が行われているかどうかが , 処理系によってチェックされます。 avolatile vo 1 ⅱ e は , 指定されたオプジェクトが , 予期せぬ場合 , 論理的に変更されているは ずがない場合においても , その値が変更さ れている可能性があることを表します。通 常 , vo ね t ⅱ e 指定されるオプジェクトは , ハ ードウェアによって変更されることが予想 されるもの , つまり , I/O にマップされてい るため不定期に更新されるメモリや , 割り 込み処理によって変更されるオプジェクト などて、す。 const と vo t ⅱ e のふたつを , ひとつの宣言 に指定することがて、きます。この場合 , そ のオプジェクトの値は , プログラムて、変更 することはて、きないが , つねに更新されて いる可能性があることを表します。 0 t0 「 s ( 宣言子 ) C コンパイラにおけるすべてのオプジェク トは , Fig. 8 に示す構文規則によって宣言さ れます。 ■ポインタ宣言 ポインタは以下の書式て宣言されます。 * 型変更子リスト ■配列宣言 配列は以下の書式て宣言されます。 識別子 [ 定数式 ] 多次元配列は , [ 定数式 ] のリストによっ て宣言され , 最初の ( もっとも左側の ) [ ] 内の定数式は省略可能てす。初期化を行う 34 CMAGAZINE 19 7 Fig. 3 Type-Specifiers type-specifiers. VOid char short i nt long float double singed unsigned struct-or-union-specifier enum-specifier typedef-name Fig. 4 テータタイプ VO id Char signed char unsigned Char short, signed short, short int, signed short int unsigned short, unsigned short int int, signed, signed int unsigned, unsigned int long, signed long, long int, signed long int unsigned long, unsigned long int float double long double struct-or-union-specifier enum-specifier typedef-name 場合には , すべての要素を指定しなければ パラメータリストによる新しいスタイル もひとつのパラメータ ( void ) が必要てす。 すが , パラメータリスト型ては , 少なくと 必要てない場合には省略することが可能て、 識別子リスト型て、は , 識別子がひとつも 識別子 ( 識別子リスト ) 2 ) 識別子リスト型 識別子 ( パラメータリスト ) 1) バラメータリスト型 関数は以下の 2 種の書式て、宣言されます。 ■関数宣 なりません。 ( ANSI スタイル ) の関数宣言は , 識別子リス トによる古いスタイル (K&R スタイル ) に比 関数プロトタイプによるチェック機能 を利用するなどの優位性があります。とく に古い処理系とのコンパチビリティを意識 する必要がない場合には , 新しいスタイル を使用するようにしたほうがよいて、しよう。 Type names ( 型名 ) 型名は , 識別子が省略された場合 , オプ ジェクトのタイプの宣言として扱われます (Fig. 9) 。

7. 月刊 C MAGAZINE 1990年7月号

List6 newdecl, 0 decl はそれぞれ宣言の木構造 のフィールドて、す。 List6 記憶クラス複数のチェックルーチ ン変更 不正な二重指定をリジェクトします。少々 わかりにくい記述になっていますが , extern と s ね tic は排他な宣言て、すからうまく動きま す。 extern remote, static 「 emote は正し い宣言て、す。 List7 構造体の違法な宣言をリジェクト 構造体の内部て、の「 emote 宣言を違法にし ます。ものすごい「ビットごとの O 日演算子」 て、すね。 GCC< は 100 行にもおよぶマクロ や , 数行にもつらなった if 条件式が平気て、て、 てきます。 MWC のプリプロセッサ , XC の プリプロセッサはこの巨大なマクロに耐え られないようて、す。 List9 木構造に「 emote を登録 実際に木構造に REM 〇 TE 属性を登録しま す。実はこれには笑えない話があります。 最初は日 EMOTE は初期化て、きないと思って いましたのて , Fig. 2 のような記述になって ところが初期化て、きることがわかったの て、 ( initia ⅱ zed は初期化されていることを示す 変数 <,specbits は記憶クラスが記録されて いる変数て、す ) , Fig. 3 のように書き換えた ら宣言をコンパイラが無視しはじめてしま いました。なぜかはみなさん考えてみまし よう・・・・・・。半日悩みました ( わかる人にはす ぐわかるかなあ ) 。 これて、 , 意味的な処理の変更は終了しま した。これだけて、はまだコードに具体的な 変更がて、てくることはありません。実際に 宣言をコードに反映させる処理は別のファ イルて、行います。リストを見ていただける とわかりますが , GCC の変数は名前が具体 的て、 , 非常に長い変数名て、す。マクロも長 く , 読みやすい記述になっています。最近 て、は長くてもわかりやすい変数名を心がけ [ 次号に続く ] ることにしています。 if (nclasses 〉 1 ) error ( " % s に記億クラスが複数です nane): else if (dec l-context ! : NORMA し & & nclasses 〉の LiSt 構造体の違法な宣言をリジェクト 0 0 0 0 0 e ー se error ((decl_context = FIE し D ? ”構造体の要素 Xs に誤った記億クラスが指定されています” (decl_context = = PARM ? " 引き数 Xs に誤った記憶クラスが指定されています " ” typena 記に記憶クラスが指定されていますっ ) . name) : ( ( 1 くく (int) RID_TYPEDEF) ー ( 1 くく (int) RID_REGISTER) specbits & : ー ( 1 くく (int) RID-AUTO) ー ( 1 くく (int) RID-STATIC) ー ( 1 くく (int) RID-EXTERN) ー ( 1 くく (int) RID-REHOTE)) ; / * nod i fy fo 「 OSK * / else if (current_binding-level = global-binding-level) if (specbits & ( 1 くく (int) RID-AUTO)) error ("top ー厄 v 引で、 % s ・は auto にできません第 nane); 関数に「 emote 宣言されたらそれをリジェクト List 卩第卩「朝朝、朝朝ョ朝ョ「「卩卩ョ朝朝第ョ物「 0 「「「 ~ い 7 「「 ~ 「朝ョ朝「朝い第メョ朝き IDENTIFIER_POINTER (declarator) ) : type = error-mark_node; decl = build-decl (FIE し D-DEC し declarator, type) : ニ FUNCTION_TYPE) else if (TREE_CODE (type) if (specbits & ( ( 1 くく (int) RID-AUTO) ー ( 1 くく (int) RID-REGISTER))) error ( " 関数、 Xs ・の記億クラスが不正です " EN 円 ER_POI NTER (declarator)) : if (specbits & 1 くく (int) RID-REMOTE) error ( " 関数は remote にできません (Xs) IDENTIFIER-POINTER(dec larator)) : / * Punction declaration not at top level. Storage classes Other than extern ・ are not allowed and 、 extern ・ makes no difference. if (current_binding-level ! ニ global-binding-level & & (specbits & ( ( 1 くく (int) RID-STATIC) ー ( 1 くく (int) RID-INLINE))) & & pedant i c) warning ( " 関数、 % s ・の記憶クラスが不正です " IDENTIFIER_POINTER (declarator) ) : = build_decl (FUNCTION DEC し , declarator. type) : decl 木構造に remote を登録 List c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c: c_decl. c: c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_decl. c- c_deel. c- c_decl. c- / * ・ s a variable. * / decl = build-decl (VAR-DECL, declarator, type) : if (inlinep) warning-with-decl (decl; " 変数、 % s ・が in ⅱ ne 宣言ですっ : / * An uninitialized decl with extern' is a reference. * / TREE_EXTERNA し (decl) !initialized & & (specbits & ( 1 くく (int) RID-EXTERN)); TREE_REMOTE (decl) = (specbits & ( 1 くく (int) RID-REMOTE)) ? 1 / * At top level, either static ・ or no s. c. makes a definition (perhaps tentative). and absence Of 、 static ・ nakes it public. * / if (current_binding-level = global-binding-level) TREE_PUB い C (decl) = ! (specbits & ( 1 くく (int) RID_STATIC)) : TREE_STATIC (decl) = ! TREE_EXTERNAL (decl) : / * Not at top level, on け、 static ・ nakes a static definition. * / e ー se CMAGAZINE 19 7 138

8. 月刊 C MAGAZINE 1990年7月号

匚ー”コ 五ロ 一三ロ はじめて学ぶプログラミンク もないプログラムがてき上がってしまいま て、す。今のところは , 内容に立ち入らず , 構造体なのてす。この例ては , 名前 , 住所 , す。したがって , きちんとしたプログラム 概念だけ理解してください 電話番号をひとかたまりのもの ( 構造体 ) と を書こうとすると , けっこうたいへんて、す。 考え , その新しい一かたまりのもの ( 構造体 型 ) の配列を考えればよいのて、す ( Fig. 2 参 また , この例てはひとかたまりとして扱 うデータの個数が 3 個なのてまだよいほうて 照 ) 。そして , 関数には構造体の配列を渡し ていくのて、す。 こて、いきなり , 構造体の すが , ひとかたまりとして扱うデータの個 配列といわれても , 面食らってしまったの 数が増えてくると , 引数が多くなり , プロ ては , 実際に住所録の構造体を宣言して て、はないて、しようか。これは , 構造体につ グラムが煩雑になってしまいます。 みましよう。 List1 を見てください。これ いて説明するための例て、あり , 心配は不要 このようなことを解決してくれるのが , て , 構造体 struct address data 型が宣言さ れました。 st 「 uct とは予約語てあり , このよ 住所録の構造体 うに構造体を宣言するときに用いられます。 Fig. 3 構造体のしくみ この構造体につけられている名前が poi nt struct address data< あり , 正確には「構造体タグ」 ↑ ( タグ名 ) と呼ばれます。また , 構造体の中 予約語 構造体タグ int X,• の各要素 ( 変数 ) name , address,tel は「メン バ」と呼ばれます。 int y; 住所録の例ては , 構造体の中にさらに配 列 ( 文字列 ) があり , 多少難しいのて , もっ と簡単な例として , ディスプレイ上の点を 考えてみましよう。ディスプレイ上の任意 の点は , 必す x 座標と y 座標からなります。 これは , まさに構造体て扱ったほうがよさ そうて、すね。 ては , ディスプレイ上の点の例て , 構造 体を宣言してみましよう。 List2 を見てくだ さい。これて , 構造体 struct point 型が宣言 されました。この構造体の構造体タグは point てす。そして , メンバは int 型の x と y てす ( Fig. 3 参照 ) 。 次に , 構造体の変数を定義してみましょ う ( 以下 , 断わらないかぎり , 変数は外部変 数を例にします ) 。構造体の最後のとじプレ ースリ〃の後には , 変数のリストを書くこ とがてきます。 List3 を見てください することによって , struct point 型の変数 a, b, c が定義されます。考え方としては a, b, c という名前がついた箱があり , その 箱には , int 型の値を入れることがてきるふ たつの引き出しがついていると仮定します。 その引き出しには , さらに , x と y という名 前がついているのてす ( Fig. 4 参照 ) 。 また , この書式は , 構文的には , int a, b, c : ととてもよく似ていますね。 はじめて学ぶ C プログラミング 119 List 1 Cd 0 「 ー 0 ワ 0 - -0 1 よ 0 乙 00 -4 ・ - -0 ティスプレイ上の点の構造体 List 2 + し十し メンバ コラム宣言と定義 宣言と定義はどう違うのでしようか ? 文章中に , 構造体を「宣言」するとか , なんとか型 の構造体の変数を「定義」すると頻繁に出てきています。しかし , 多くの読者の方は , その違 いを気にしないで , なに気なく読みすごしているのではないかと思います。いったい , 「宣 と「定義」の違いとはなんでしようか ? 簡単にいうと , 「宣言」は指定した属性と名前とを関連づけるだけのものです。それにたい して「定義」は属性をつけるとともに , メモリに領域を割り当てるのです。したがって , 「定義」 は暗黙のうちに「宣言」を含みます。 内部変数では , 宣言と定義が同時に行われます。一般的に内部変数は , 「宣言」するといい ます。本連載でも , 変数宣言として何回も取り上げましたね。たとえば , 「関数の中」で , int X : とすることにより , この関数が呼び出されたときに , int 型という「性質」をもった内部変数 x が , 「メモリ」に領域が割り当てられるのです。この変数 x は , 内部変数なので , 関数呼び出し が終了すると , 当然 , メモリから消減します。 ところが , 外部変数には , 宣言と定義の区別があります。このことについては , すでに説 こで , 簡単に復習してみましよう。 明しました。 外部変数の定義は , 任意の関数の外側で , 一度だけ行われます。たとえば , 任意の「関数の 外」で , int X : とすることにより , 内部変数の場合の変数宣言と同様に , int 型という「性質」をもった変数 x が , 「メモリ」に割り当てられるのです。これを , int 型の外部変数 x の「定義」といいます。 これに対して , この外部変数を参照する関数では , その「関数の中」で , extern int X : のようにします。このとき , 変数 x には , 実際には , メモリ割り当ては行われません。変数 x には , int 型だという性質のみが指定されるのです。これを int 型の外部変数 x を参照するための 「官言」といいます。 同様にして , 構造体について考えてみましよう。構造体を考えるときには , 「宣言」と「定義」 を区別します。 List2 のように構造体を「宣言」すると , 構造体のテンプレート ( 構造体の形 ) だけが宣言さ れ , メモリには何も割り当てられません。しかし , 構造体タグをつけているので , 以下のよ うにすると , struct point 型の変数 a を「定義」できます。 struct point a : このとき , 変数 a は , st 「 uct point 型の性質をもち , 実際にメモリに割り当てられます。ま た , List3 のようにしても , struct point 型の変数 a. b, c が定義できます。この変数の定義

9. 月刊 C MAGAZINE 1990年7月号

LANGUAGE 致命的工ラー処理の 1r0 れ 5P0 「 e れ蓄 ( 「 E 汁 0 「 Ha れ引 ing Danny 、 awrence/ 岩谷宏訳 COMPUTER LANGUAGE/April 1989 ) 提携記事 BITST—SNC 透明化 ー物題満 Tm い : ・ CA 孖を INA ー田日 E 日 A S 致命的工ラーの処理は , それぞれのプロ グラムに合った方法て行うべきて、す。 MS- DOS が画面に出す「中止く A 〉 , もう一度 く R 〉 , 無視く I> ・ ? 」というメッセージて、十分 なプログラムもあるて、しよう。その一方て、 , ウインドウをオープンしてメッセージを画 面の下に出す , などの複雑な処理を必要と するプログラムもあります。このように プログラムによって要件が違うために , 致 命的ェラーのハンドラは , プログラムごと に作るのが普通て、す。ただそれは , アセン プリ言語て、書いておいて , プログラムごと にユーザインタフェイスの部分だけを書き という場合が多いのて、 , そ 換えればよい れほどめんどうな作業て、はありません。 本稿ては , 汎用的な致命的工ラーハンド ラ用の低レベルの細部的な処理を引き受け , ューザインタフェイス関数はそのつど自由 に書ける , というシステムについて説明し ます。 MS-DOS の 致命的ェラー処理 MS-DOS が INT21H のファンクションの サービスを実行しているときに , 深刻な工 ラーが起こると , 必ず致命的工ラーハンド ラをコールします。このハンドラは , ユー 22 CMAGAZINE 19 7 ザにエラーを説明して , 指示を待つものて、 す。デフォルトのハンドラは , おなじみの 「中止く A 〉 , もう一度く R 〉 , 無視く I> ? 」メ ッセージて , ューザによって囚 , 囮 , または 国キーが押されるのを待ちます。これとは 別のエラー処理をさせたいときは , ハンド ラを自分て、書かなければなりません。 ハンドラに制御が渡ったとき , スタック とレジスタにはエラーに関する情報と , MS TbI.1 スタックの内容 SP からのオフセット 十 00h 旧 十 02h CS 十 04h Flags 十 06h AX 十 08h BX 十 Oah CX 十 Och 十 Oeh 十 1 Oh 団 十 1 2 h B P 十 14h DS 十 1 6 h ES 十 1 8 h 旧 十 1 ah CS 十 1 ch TbI.2 FIags DX MS ー DOS へのリターンアドレスとフラグ 旧 ET で MS ー DOS ヘリターンする アプリケーションのレジスタ ー DOS とユーザアプリケーションへの , 両方 のリターンアドレスがあります。スタック のようすを TbI. 1 に示します。スタックトッ プは , MS ー DOS への旧 ET 用に設定されてい ます。その下に , アプリケーションのレジ スタ , リターンアドレス , そしてフラグが あります。 レジスタには , 以下に述べるようなエラ ーの理由と所在に関する情報が入っていま す。 ・団レジスタの下位バイトには , 工ラーコー ドがあります。上位バイトは未定義です。 Tb に 2 に , 工ラーコードとその意味を示し ます ・ BP : に , 工ラーを起こしたデバイスの デバイスへッダ ( = デバイスドライバのヘ ッダ部 ) の制御プロックへの far ポインタが あります アプリケーションへのリターンアドレス にあるエラーコード 10 進数 工ラーコード 書き込み保護のティスクに書き込もうとした そういうドライプ ( ~ 装置 ) は存在しない ドライプの準備ができていない ( 装置のドアが開いているなど ) 存在しないコマンド ( テパイスドライバがサポートしていないリクエスト ) データの CRC 工ラー リクエストのサイズの不正 シークエラー セクタが存在しない プリンタが用紙切れ 書き込み工ラー 読み出し工ラー 一般的なティスク不良 [ 注 ] 、、リクエスト″とはテパイスドライバへのレ O リクエストのことである。 10 9 8 6 5 4 3 2 1 0

10. 月刊 C MAGAZINE 1990年7月号

り一般性をもたせるために , st 「 uct point 型 て、きることがわかると思います。 す。この , アルゴリズムは , みなさんよく の各メンバの型を dou ble 型に変更しまし ご存じ ( ピタゴラスの定理 ) て、しようから , た。また , 新たに , 長方形を表す構造体 st 「 uct 説明する必要はありませんよね。 rectang 型を宣言しています。長方形は対 角線上の 2 点の座標て表せます。したがっ て , このように , ふたつの struct point 型の List8 や List9 のように , プログラムを書く 構造体変数 pl , p2 をメンバにすればよいの たびに , いちいち構造体の宣言を書いてい て、す (Fig. 7 ) 。 たのては , とてもめんどうてすね。何度も 次に , 関数の戻り値が構造体の場合を考 ListII は , struct P0int 型の引数をふたっ 使う構造体や関数のプロトタイプ宣言は , えてみましよう。構造体を関数の戻り値と 受け取り , その 2 点間の距離を求める関数 ヘッダファイルにまとめたほうが便利て、す することもて、きるのてす。これも , 今まて distance てす。まず最初に , math. h と し , 管理もしやすいものてす。構造体の宣 と変わりありませんね。 point. h(List10) をインクルードします。イ 言と関数のプロトタイプ宣言をへッダファ それては , List9 を見てみましよう。これ ンクルードすることによって , このファイ イルにまとめ , それらを用いた簡単なプロ は , int 型の変数をふたっ読み込み , それを ルの中て、宣言されている構造体 struct point グラムも作ってみましよう。 struct point 型の変数 a に代入し , それぞれ 型を用いることがて、きるのてす。この関数 今まて出てきた構造体の宣言と , 関数の x 座標と y 座標にします。その際に , struct の変更点は ,struct point 型のメンバを int 型 プロトタイプ宣言にれから作るものも含む ) point 型の戻り値を返す関数 set ー point を使っ から doub 厄型に変更したのて、 , 計算すると を , List10(point. h) という名前のヘッダフ ています。そして , 代入されていることを きに double 型にキャストする部分がなくな ァイルにまとめました。今まてに作ってき 表示して , 確かめています。このプログラ っている点て、す。それ以外は , List8< のも た構造体と関数に少し変更があります。よ ムを実行すると , 確かに構造体を戻り値に point. h 関数の戻り値に構造体を使用した例 1 : / * 基本的な平面図形を表す構造体宣言 * / 2 : 3 : struct point { 4 : / * x 座標 * / doub I e x : / * y 座標 * / 5 : double y; 7 : 8 : 9 : struct circle { ・ / * 中心座標 * / struct po int center, / * 半径 * / double radius; 13 : 15 : struct triangle { 16 : struct point pl ; struct point p2; struct point p3; 20 : 22 : struct rectangle 23 : struct point pl; 24 : struct point p2; 25 : } ; 26 : 27 : 28 : 29 : / * プロトタイプ宣言 * / 30 : 31 : / * 2 点間の距離 * / 32 : double distance( struct point, struct point ) : 33 : 34 : / * struct point 型の動的初期化 * / 35 : struct point set_point( double, double ) : 36 : 37 : / * 円の面積 * / 38 : double circle-area( struct circle ) : 39 : 40 : / * 三角形の面積 * / 41 : double tgl-area( struct triangle ) : 42 : 43 : / * 長方形の面積 * / 44 : double rect_area( struct rectangle ) : List 9 List 1 0 1 : #include く stdio. h> 2 : 3 : 4 : struct point { 5 : i nt X : 6 : int y; 8 : 9 : 10 : struct po int set-PO int ( i nt, int 13 : void main( void ) struct point a; int x, y; printf()x ー scanf("%d" printf("y scanf("%d" 22 : 23 : 24 : 26 : 28 : 29 : struct POint set-point( int X, int y 30 : struct point pt; 32 : 33 : 34 : pt. x 35 : pt. y 36 : 37 : return pt : 38 : } 形 1 《 2 角標標標 三座座座 set-point( printf("a. x = %dYn" printf("a. y = %dYn", 形 12 方標標 長座座 a a. x ) : 122 CMAGAZINE 19 7