特集 しかし , まったく空白と区別のつかない こともあります。このリストが , タブがス ペース 4 個間隔になるという仮定て、書かれた ソースだとしましよう。タブがスペース 8 個 間隔になるような表示て、これを見ると , Li st 15 のようになるて、しよう。 これは , 単にインデント幅がスペース 4 個 から 8 個になっただけて、すが , これならあま り気にならないかもしれません。しかし , フフまくいくとはかぎりません。た とえば , 変数の定義の箇所て、 , 変数名を揃 えたくなる人は多いようて、す。また , コメ ントの始まる位置も揃えたくなるようて、す。 Char int POS タブをスペース 4 個間隔のつもりて、書いて いる人がこのように書いた場合 , char の後 にあるのはタブが 1 個 , int の後にあるのはタ プが 2 個となります。 int というのは 3 文字な のて、 , タブが 1 個て、は次のようになってしま います。 Char int pos 同じリストをタブがスペース 8 個間隔の人 が見ると , 次のリストのようになりますが , これはちょっと見にくいのて、す。 Char int static int lcount int static int lcount Char POS 多分 , 次のように書きたくなるはずて、す。 POS わざわざすらせて合わせるべきて、しようか。 数を追加したくなった場合 , ほかの変数も static unsigned long の変 て、は , さらに Char int static int POS lcount static unsigned long wcount 0 この後て , やつばり lcount や wcount は , 外部変数にして , ソースプログラムの頭に 書こうと思い , 2 行削った後が , cha int POS char int POS それて、は , タブがスペース何個に相当す るかにかかわらず , あまり見苦しくなく表 示されるように書くにはどうすればよいて、 しようか。 簡単て、す。タブを使わなければいいのて す。 リストの空白を , タブて、はなく空白て、表 現すれば解決します。 ところが , この場合も , いったい空白を いくつ置けばよいのかが問題になります。 たとえば , 次のような 1 行を追加したくなっ たら , どうしましようか。 static int lcount 変数名の書き始める位置にこだわってい る人なら , 多分次のように書くのは見苦し いと思うて、しよう。 というのは , さすがにみつともないのて , また左につめるのて、しようか。 こういう作 業はやってられない というのが正直なと ころだと思いますが , ぶつぶついいながら , ちゃんと揃える人も多いかもしれません。 あるいは , この程度なら後て、揃えるための ソフトがあるかもしれないし , そういう機 能を備えたり , マクロて、処理て、きるように したエデイタがあるかもしれません。 しかし , 私はこういう手間に神経質にな るのは不本意て、はないかと考え , 長年の研 究の結果 , ついに次のように書くことを結 論するに至りました。空白はひとって十分。 べつに見にくくもなんともないと思い込む のが秘訣てす。と書きましたが , 改めてこ うして見てみると , そんなに見やすくない 書くことになります。という余談はともか ように書いてあるのて , 自然にこのように R の書き方を真似していると , 最初からこの ちなみに , 別に長年研究しなくても , K& static unsigned long wcount static intlcount int pos char *S , ような気もします。 0 C プログンの秘 く , なぜ前記の表現があまり見やすい気が しないのか考えてみますと , どうも , static unsigend long という前書きがあまりに長すぎて圧倒され てしまい , 肝心の変数名があまり目立たな いからのようてす。このような場合には , typedef unsigned long ULONG とて、もしておけば , 次のようになります。 char *S ; int pos 特集 C プログラミングの秘訣 59 わかる程度に , しかし目をあちこち動かさ れません。インデントの幅は , ばっと見て ぎることになり , 疲労の原因となるかもし デント幅が広すぎると , 目が左右に動きす 苦しくなってしまうからてす。また , イン ってしまい , ついに折り返して , かなり見 ントを行うと , 書く内容がどんどん右に寄 なぜならスペース 8 個を 1 段としてインデ 多いものてす。 スペース 4 個分の間隔としたい , という人も それて、も , C のプログラムのインデントは , ーディングする人が多くなりがちて、すが , そのため , タブをスペース 8 個と考えてコ ませんが ) 通常そのようになっています。 ど , ( 何か技を使えば変更てきるのかもしれ MS-DOS の type コマンドを使ったときな は , かなり頻繁にあるようて、す。たとえば 実際 , タブがスペース 8 個とみなされること 8 個として書かれているものとみなします。 ただし , 今度は , これがタブをスペース ましよう。 List 16 を見てください ンデントがもっとひどくなる例を考えてみ に対する文字数が異なることによって , イ またまた話題を元に戻しましよう。タブ た。 りが揃うようにタブを使って書いていまし 最近のことて、 , かなり長い間 , 変数の始ま 身 , このように書くようになったのがつい 異論のある人も多いて、しよう。実は , 私自 によって意見の分かれるところて、すから , ただ , このあたりの感覚は , かなり個人差 この程度なら , あまり変に見えません。 0 static ULONG wcount static int lcount
lnformation from Compiler Makers 作成され , これを使って . COM ファ というエラーになります。シンポ イルのシンポリックデノヾッグがて ル情報を付加するオプションはち きるようになります。 ゃんと指定していますが , なぜで しようか。 A Q C 十十で Turbo Vision のプロ . COM ファイルは , . EXE ファ イルのようにフディルの先頭に情 グラムを作成しているのですが , 報を含むことがて、きません。した メニュー項目を選択したとき一段 がって , デバッグ情報を . COM ファ 深いメニューを表示させる方法が イルの中に付加することはてきま わかりません。どのようにプログ せん。 . COM ファイルをデバッグす ラミングすればよいのでしようか。 るためには , いったん . EXE ファイ A ルを作成して TDSTRIP コマンド List 2 に , Turbo Vision を使 て一 s ( シンポル情報の取り出し ) , ってネストされたメニューを作成 -c(. COM ファイルへの変換 ) オプシ するためのプログラム例を示しま ョンを同時に指定してください すのて、参考にしてください 0 拡張子が . TDS という Turbo Debu gger 用のシンポル情報ファイルカす List 2 〃 Atmt コマンド 41 : const cmAtmt = 100 ; 42 : 43 : 〃 44 : 〃クラス定義 45 : 〃 46 : class DemoApp : public TAppIication ( 47 : public: DemoApp(); 48 : 49 : static の : 〃メニューの初期化 ″イベント処理 void handIeEvent( TEvent &event ) : / / About ポックス void Atx)ut&)x(); 0 、ー - りん ′ 0 「 0 53 : 54 : } : 55 : 57 : / / クラス DemoApp のコンストラクタ 59 : DemoApp::DemoApp() : TProgInit(&TAppIi(htion: :initStatusLine. &TApplication: 60 : 61 : ( 62 : } 63 : 64 : / / 65 : / / メニューバーの定義 66 : 〃 67 : TMenuBar (I)emoApp: :initMenuBar(TRect r) r. b. y = r. a. y + 1 : 70 : TMenuBar *newMenu = new TMenuBar(r, 本 n TSubMenu( ”¯S¯ystem ” . kbAltS) + 71 : TMenuItem( ”¯A-tx)ut ”を cmAtmt, kbAltA, hcNoContext) + newLine() + 73 : / / 他のメニューを呼び出す TMenuItem TMenuItem("Exit ” , 0 , new TMenu( TMenuItem( ” Exit & ¯s-ave ” , cmQuit, kbAItX, hcNoContext, 0 , new TMenuItem( ” Exit & ーÅー面 on ” , cQIit. kbAItY, hcNüntext. 0 , 77 : new TMenuItem( ” Just Q-uit", kbAltZ, hcNüntext, 0 ) ) ) ) , hcNoContext) 79 : 80 : 83 : 84 : / / 85 : ″イベント処理ルーチン 87 : void DemoApp: :handIeEvent(TEvent &event) 89 : TApplication::handIeEvent(event); if (event. what = evCo•nd) 91 : switch (event. message. comand) { 92 : / / AtX)ut case : コマンド Atx)utBox(); 93 : cIarEvent (event) : 94 : break : 1 : / / モードっき Atnut ダイアログボックスの表示 101 : / / 102 : void 103 : ( 104 : TDiaIog 事図 : new TDialog(TRect(0. 0. 35. 12 ) , 105 : if ( 〆 ) ( 1 : #>insert(new 2 , 34. 7 ) 107 : ” \ 00 釘 ur V is ion \ n \ 003 \ n ” 0 List 2 ” \ 003Cr ti a nested nu \ n \ 003 \ n ” "*003BorIand Techn ical ” ) ) ; K+>insert(new TButton(TRect(8, 9. 27 , 11 ) , "¯O¯K"' cm0K, bf[hfault)); gi->options ト 0fCentered; deskTop->execView(EO; 108 : 109 : 110 : 111 : 112 : 113 : 114 : 115 : 116 : } 117 : int min( VOid ) 118 : 119. 120 : DemoApp App; 121 : return 0 : 122 : 123 : } return newMenu; Fig. 1 inte 「「 upt 修飾子を使った関数に渡されるレジスタの内容 VOid interrupt handler(unsigned bp, unsigned di, unsigned si, unsigned ds, unsigned es, unsigned dx, unsigned cx, unsigned bx, unsigned ax, unsigned ip, unsigned cs, unsigned flags, 95 : Fig. 2 Windows のスクロールバー指定例 SetScroIIRange(hwnd, SB VERT, 0 , 1 0 , FALSE) : SetScrollPos(hwnd, SB VERT, 1 , TRUE) : lnformation from Compiler Makers 159
五ロ C の道具箱 サ を ロ示 、 , 健 ~ 「そ 』レ、。。永択、紹 ノ〈 ( ( , 国選す ~ を 豊をせ↓ で示 上表 画れウ ド 回イ 前今ウ 第 22 回 0 sc 「 0 膩 ) 関数 ( ウインドウの 1 行スクロール , CM920401 ) 1 : #include ” config. h ” 3 : #define MYEXTERN extern 4 : 5 : #include く stdio. h> 6 : #include く stdlib. h> 7 : #include く string. h> 8 : #include ” curses. h ” 9 : 10 : #ifdef PROTOTYPE ” cboxprot. h ” 11 : #include 12 : #endif / * ウインドウの 1 行スクロール * / 14 : 15 : sc 「 011(WINDOW * ) int i 17 : 18 : unsigned xlen 19 : unsigned ylen 20 : unsigned linel 21 : uns igned cdmemseg ; 22 : uns igned cdmemoff 23 : uns *. atmemseg ; 24 : uns atmemoff 25 : unsigned spcseg 26 : spcoff : 27 : char spcline [ 501 ] : 28 : char *spcln; nemset(spcl ine, 0X20 , 50 の : 29 : s 1 Ⅱ = (char *)spcline ・ = の { if(pw->scroll return(-l) : 33 : cdmemseg = FP—SEG(pw->psavecode) ; 34 : cdmemoff = FP—OFF(pw->psavecode) ; 35 : atmemseg = FP—SEG(pw->psaveattr) : 36 : atmemoff = FP—0FF(pw- 〉 psaveattr) : = FP_SEG(spcIn) ; 38 : S pcseg = FP_0FF(spcIn) ; 39 : spcoff xlen = pw->xlenn ー (KEIWIDLR * 2 ) ; 40 : ylen = pw->ylenn ー (KEIWIDTD * 2 ) ・ 41 : 42 : 1 ⅲ el = xlen * 2 for(i=O ;i く ylen-l ; i + + ) { movedata(cdmemseg, cdmemoff + 1 inel*(i + 1), linel ) ; cdmemseg, cdmemoff + linel*i novedata(atmemseg, atmemoff + linel*(i + 1), 46 : List 1 ファイル表示プログラム 今回は画面上て、選択したファイルをサプ ウインドウに表示するプログラムの作成を 試みた。 もちろんこのプログラムは MS-DOS, U N Ⅸ両用のものて、ある。 ファイルをサプウインドウ内に表示する 場合に問題になるのがスクロールて、ある。 本連載て、は , ' 91 年 1 月号において , サプウ インドウ内て、のスクロールを実現する関数 として scroll という関数を紹介した。 この関数はバックスクロールの機能まて 実現しているが , 実は UNIX 版 curses におい ても同名の関数が存在する。しかし , これ は単に 1 行分だけサプウインドウを上にスク ロールさせるものなのて , 両者の間には互 換性がない そこ <MS-DOS 版 curses 用に r 。 11 ( ) 関 数を書き換えることにした。 List 1 がそのプ ログラムて、ある。 この関数は , サプウインドウの内容を格 納しているバッフアの内容を上に 1 行分だけ ずらすことによって実現している。単にず らすだけて、は , 最終行とその上の行が同じ 内容になってしまうのて , そこに空白行を 付加している。 126 C MAGAZINE 1992 4
djg 詳解講座・Ⅷ 0 刪 World 上の x 絶対座標を 8 て、割った余り ( 7 とのビッ ト AND をとったもの ) てす。 クラス GrRegion には , リージョンの絶対 座標そのものを表す変数はありません。リ ージョンの左上の点のアドレス ( ? data) と x mod からリージョンの絶対位置が決定さ れます。そして , plane size, row scale に よりメインメモリと VRAM との違いを吸収 . 5 Fig. 3 領域のコピー法 矩形の横ドット数を w sx =S—>x—mod dx=D—>X—mod とする 矩形 S SX ↓ バイト境界 しているわけて、す ( Fig. 1 ) 。 領域のコピー法 矩形 S を合同の矩形 D にコピーする作業を もっとも簡単に行うには , Fig. 2 のようにす るのがもっとも安直て、すが , に遅くなってしまいます。そこて , この方 これて、は非常 ろいろとありますが今回は省略します。 場合 , xor 転送のサポートなど , 留意点はい 矩形 D てまたがっているバイトの数が異なる にも , 左右の端における特別処理 , 矩形 S と らスキャンしていくことになります。ほか フトがすべて左シフトになり , 矩形 S を左か トアラインメント上〃左にある場合は , シ Fig. 3 の例とは逆に矩形 S のほうがバイ す。 ルゴリズム (Fig. 5 ) を繰り返せばよいわけて (b) なる unsigned char 型変数とし , 転送ア ャリー用マスクをこれと 0 と 1 が逆の Fig. 4 ig. 4 (a) のような unsigned char 型変数 , キ 側に次々に回される ) を使えば , マスクを F こばれたビットは補充 鉛筆の芯のように シフトてなく , ローテートシフト ( ロケット こばれたビットが捨てられていく単純な 転送時に使います。 てこばれた分は carry に記憶しておき , 次の ーと合わせて ) 矩形 D に転送します。シフト だけシフトしたものを ( 二度目以降はキャリ ます。すなわち , 矩形 S のバイトを diff の数 右の端から順に右シフトを繰り返して行い ます ( この差を diff , diff=dx—sx)o 転送は バイトアラインメント上〃右に位置してい Fig. 3 て、は , 矩形 S よりも矩形 D のほうが バイト単位て、領域を転送します。 法は矩形の幅が狭いときだけにし , 通常は 矩形 D : バイト境界 dx ↓ ロロロロロロロロロロ・・ 縦線は 8 ドットごとのバイト境界を表す Fig. 4 Fig マスクとキャリー用マスク (a) 0 1 0 1 1 0 SX 十 W ・ ・・ロロロロロロロロロ dx 十 w 0 1 8 個 転送アルゴリズム S, D を右に 1 バイト分ずつずらす { 最初 S は矩形 S の左端 , D は矩形 D の左端 D キャリー (S 「 0 「 diff) & マスクーキャリー (S 「 0 「 diff) & キャリー用マスク ( 「 0 「は右ローテートシフトのこと ) ところて , 矩形の幅 w が狭いときには , A : 「矩形 S は全体がひとつのバイトに 納まっている」 B : 「矩形 S は全体がふたつのバイトに またがっている」 a : 「矩形 D は全体がひとつのバイトに 納まっている」 b : 「矩形 D は全体がふたつのバイトに またがっている」 可 gcc 詳解講座・ H 0 GCC World 85 としたとき
K & R の第 1 版て、は , return ( 式 ) ; default : break ; case constant—expression . switch (expression) { ・ switch 文の書き方 C{ ドの位置に注意 ) } while (expression) ・ do { •do wh ⅱ e 文の書き方 ( 、、 { ドの位置に注意 ) while (expression) れを同じ行に書かずに , 必ず次の行に書き が複文て、ない場合 , および空文のとき , そ for 文と同様 , 次のように , while 文の本体 while (expression) { ■ wh ⅱ e 文の書き方 ( 、、 { ドの位置に注意 ) 書いてはいけません。混乱の元になります。 に , 空文を意味するセミコロンを同じ行に に書かずに , 必ず次の行に書きます。とく い場合 , および空文のとき , それを同じ行 List 19 のように , for 文の本体が複文て、な ません。これは慣用句のようなものて、す。 にかぎり , 空白なして、詰めて書いてかまい は , 空白を開けますが , 無限ループの場合 for の中の式を区切るセミコロンの後に 注意 ) ー for 文の書き方 (List 18 , ドの位置に と , return の後のカッコはなくなってしまい しかし , 第 2 版になる 思うのてすが・・・ せん。かえってわかりにくくなるだけだと がわかりやすいのか , まったく理解てきま りますが , 私にはなぜカッコがあったほう ほうがわかりやすいから , というものがあ く耳にする理由としては , カッコがあった がついていたのか , よくわかりません。よ のように表記されていました。なぜカッコ break プログラミング言語 C 第 2 版 } else { } else if { Fig. 1 K & R 第 2 版 けて , 区別されているため , 簡単に識別て、 効範囲の内部は , 一段深くインデントをつ 囲になっている , という点にあります。有 、、ドまて、が , そのキーワードに対する有効範 ぐ下に向かって眺めたとき , 最初に現れた do, switch, (f) の先頭の文字から , まっす 書き方の基本は , キーワード (for, while, すて、に述べたように , これらのカッコの if (expression) { ■ if 文の書き方 (• { ドの位置に注意 ) て、しよう。 て , 目立つようにすると覚えておけばよい 書くときには , インデントをひとつ解除し は switch と case は同じ高さて、す。ラベルを h より少し後に書く人もいますが , K&R て、 これ以外の書き方としては ,case を switc す。 じ位置に揃えて書きます。 default も同様て、 す。 switch 文に対して , case ラベルを , 同 これが結構異論のあるところだと思いま ANSI 規格準拠 W 功—. ニハン / 0 リッチ - 著 物第第第幸物意 み参物 。 ~ 立出版株式会社、 ン第を 実際は , 左に空白を置きません。もっと も左から書き始めます。 関数の定義の場合 , 始まりのカッコ、、ドを 関数名の定義行に書かないのは , すて、に述 べたように , 関数の始まりを検索しやすく する効果を持っています。 また , 関数の型は , 関数名と同じ行に書 きます。こうしておくと , grep のようなツ ールて、関数名て、行単位の検索を実行したと き , 型がついたまま表示されて便利て、す。 ・変数定義部分と , それ以後の部分は , 空 白行によって分離する ( 2 ) 。 unsigned int f00 (int count) きます。 ・関数の定義の例 C{ ドの位置に注意 ) unsigned int f00 ( ) int total count > > = 1 ・ tot 引十十 ; while ((count & 1 ) return totaF char* Str , いけません。 上のように書きます。次のように書いては TAB て、揃えたりはしません。ポインタは , 型名の後に , 単に空白をひとっ空けます。 Char *str , FILE *fp ; ■変数の宣言の仕方の例。 〇 char keyword[10]; >< char keyword[10] ■式文を表す、、 ; ″の直前には空白を空けな 〇 arrayC10] X arrayu [ 10 ] ・配列の、、 [ ] 〃の前後には空白を空けない。
onfig. h を入れておく必要がある。また統合 環境の中て、コンパイルするのて、はなく ,TC C を使用する。 tcc -v -K -J -w -ml -c ( デバッガを使用 /char を unsigned cha r にする / 漢字を使用する / 警告あり / ラー ジメモリモード / リンクは行わずコンバ イルのみ行う ) ・ TurbO C 用 config. h #define TURBOC #ifndef MSDOS #define MSDOS #endif #ifndef PROTOTYPE #define PROTOTYP E wtypew. exeC 表示したファイル Fig. 3 くくファイルの表示 > > s c r 0 1 ー T$TO 【書式】 ウィⅢ 81 ude 終ÄincIude く stdiO. h> äinclude く stdlib. h 〉 äinclude く string. h> Ⅲ nclude ” curses. h ” Ⅲ fde 「 PROTOTYPE ウインドウの 1 行スクロール ” conf ig. h ” 終了 ESC 選択 C R 次画面 TAB 前画面 B S Fig. 4 U N Ⅸ上での sc 「 0 Ⅲ exe の実行画面 #endif MS-C MS-C て、コンパイルする場合には , 従来と 同様にコンパイルスイッチを設定するとと もに , MS-C コンパイラのディレクトリ (M (C) 下の INCLUDEi•ィレクトリの中に con fig. h を入れておく必要がある。 CL /AL /Od /W2 /Zi /J /c 関数 . c ( ラージメモリモード /CodeView を使用 / 警告レベルは 2/char を unsigned char に する / リンクは行わずコンバイルのみ行 鰍 x 3 れ ・唸氿ー町田ま 亂 0 リ t t を虹 . C 0 第 . 8 懇 ー V 駅謝 師嘯 tP. C 計 P. 1 TE 訂 1 . 8 E E. H し A 間 . C 町一層 . C 3. 0 trans 制 2 は 0 州 07. C 0 Ⅲ S. C CT 山 C 03. C ー E . C [ 0 」 TF 〔 . C [ 雅田 E C 町一順 . C ー「 a03 制 . 0 tr 3 制 2 第圏 . C ー SPCKEY C 価 F ( . C ー y. れ TESTI. C 第 E 用 2. C 000Y2 ⅱ nd ー 0 リ 00 を門 . C 層ー 00k をれ C Fig. 5 U NIX 上での wtypew. exe の実行画面 ・ MS-C 用 config. h #define MSC #ifndef MSDOS #define MSDOS #endif #ifndef PROTOTYPE #define PROTOTYPE ー日 5 第代 00Y ・を一 5 第 0 北 を y. S 盟尺されました ー町フィンドウの表示ー ) T A B VIDIN ー〉 CRO SPCTO 終 TESTO 1 供 P [ 0 介 F EF 貶 C 町 E 灯順 . C tr 3 制 2 #endif 道具箱に追加された関数を UN Ⅸ上て、コン パイルし , 特定のライプラリ ( たとえば libc b 。 x. a ) に追加するには , 以下のような操作 を行えばよい。なお , 当然のことながら , 132 C MAGAZINE 1992 4 ー爲 CA に . C 町ー物 . C ー 0 ー 0 ーを . 0 3. 0 t 「 an ー制 2.0 Cu 「 . 0 「 00Y2 れ nd t0000 を門 . 0 30 「 y. 0
員 mo マイクロソフトの言語シリーズ。 べースとなる型を宣言するのが specifier-q コロンのうしろの定数式て、与えられる値 ualifier-list の部分てあり , 識別子とそれに は「幅」と呼ばれ , そのビットフィールドに 対するポインタだの配列だの関数だのとい 何ビットを与えるべきかを指定する。この う部分を宣言するのが struct-declarator-li 場合 , a というメンバは 1 ビットが割り当て st< ある。 られ , b には 6 ビットが割り当てられ , c には 構造体のメンノヾ宣言てある struct-declar 2 ビット , d には 5 ビットが割り当てられる。 ation は , 通常の変数宣言と同じてあるかの ビットフィールドは構造体のメンバとして , ように思うのだが , よく考えると違いがあ 普通のメンバと同じ表記て、アクセスするこ る。それは記憶クラスを指定てきない とがてきる。当然値を参照するだけて、なく , と , 今問題にしているビットフィールドが 代入することも可能て、ある。先の例て、いえ 用意されることて、ある。 specifier-qualifier ば , 次のような操作が可能て、ある。 ー list は記憶クラスを排除するために設けられ bits. b bits. c , た構文則てある (Fig. 7 ) 。 bits. a そして , struct-declarator-list は struct- ビットフィールドの特徴は , それがたと declarator を 1 個以上のカンマ、、 , 〃て、区切って えメモリワードの真ん中に割り付けられて 並べたものてある。たとえば \'int i, j ; な いるとしても , その値はそれとは無関係に どとしてメンバを宣言する場合 , i と j がそれ あたかもその幅の int ( 符号については後述 ) ぞれ struct-declarator て、あり , 当 , j ′′て、 stru て、あるかのように取り扱われるということ ct-declarator-list を構成することになる (F て、ある。すなわち 1 ビットの幅の符号なしフ ig. 8 。なお , struct-declarator は最低 1 個て、 ィールドの値は常に 0 か 1 て、あり , 2 ビットの もよいのて、 , ynt i ; 〃の場合 , 当〃は struct 幅の符号なしフィールドの値は 0 , 1 , 2 , 3 -decl arator て、もあり , struct-declarator-l のいずれかてある。これは , 処理系の側が ist< もあることになる ) 。 必要なシフト操作とマスク操作を行ってく 最後に , やっと struct-declarator にたど れることを意味する。もっとも最近の CPU りついたが , これは 2 種類ある。最初の dec はビット列操作命令を備える機種が増えた larator は通常の変数宣言の構文そのものて ため , そのような CPU< はシフトもマスク ある。なお , 構造体宣言の内側て、関数を宣 も行われないだろう。 言することはてきないが , これは構文上て こては型を int と宣言しているが , ビッ はなく , constraints( 制約 ) として禁じられ トフィールドは int, signed int, unsigned ている (Fig. 9 ) 。 int のうちのいずれかの型を持たなくてはな 2 番目の形式がビットフィールドてある。 らない。ただし , 単に int と指定した場合 これては何のことだかよくわからないのて , に , 実際にそれが符号っき ( 割り当てられた 少し実例を上げてみよう。 ビット幅の最上位ビットを符号ビットとす struct { る ) てあるかどうかは処理系定義となってい int る。したがって , 符号の有無が重要な場合 int には signed/unsigned をきちんと指定するほ うがよい。このルールは普通の型の場合と は違うのて注意を要する。通常の int は sign ed int とまったく同じてある。またビットフ ィールドを配列にすることはてきないし , アドレスを取ることもてきない。すなわち ツールの作成からアプリケーションまで、 プログラム開発を強力にサホートします。 プロジェクト指向 プロフェッショナルシリーズには、共通の開発 環境 Programmer's Work Bench(PWB) を装備。煩わしいソースコード、メイクファイル などの管理は PWB まかせ。しかも統一され た開発環境のもとて、複数言語によるプログラ ミングやデバッグが可能て、す。マイクロソフト だからて、きるトータルソリューションてす。 やさしい統合環境 Quick シリーズには抜群の操作性を発揮す る統合環境を装備。コンパイルやリンクも、 ドロップダウンメニューやファンクションキー により、ェタ画面から直接行えます。しかも、 オンラインのアドバイザやチュートリアルを標 準装備するなど、プログラマーの使いやすさ を考えた開発環境てす。 1 6 3 2 0 5 int マイクロソフト マイクロソフト株式会社 〒 1 聞東京都新宿区西新宿 7 ー 5 ー 25 K ビルテング 電話 ( 03 ) 3363-1201 int } bits : ANSI C ー more 123
ロックを割り当てるときには , リストの先 割り当てられたものてあることを確認する トの頭を使った循環リストによって表現し 頭から , 必要なバイト数より大きい空きプ のに使います。 magic には必ず MEM MAG ます。ヒープ領域は Fig. 4 のように管理され ロックが見つかるまて、スキャンします。そ IC という値がセットされているはずなの こて、は , ヒープ領域 ることになります。 して , 空き領域から必要な分だけ取り除い て、 , もしこの値が MEM MAGIC て、なけれ は三つのプロックに分割されています。左 て , 残りは再びリストに返します。 ば , そのプロックは allocate block が割り当 端のプロックは , 400 バイトの大きさて空き てたものてはないか , あるいはヘッダ情報 たとえば , Fig. 4 の状態て、 50 バイトの要求 プロックてす。中央のプロックは , 400 バイ を受け取ったら , 左端の 400 バイトの空きプ トの大きさて使用中てす。右端のプロック が壊れてしまっていることを意味します。 このアルゴリズムて、は , すべてのプロック ロックのうち , 先頭の 50 バイトを割り当て は 200 バイトの大きさて空いています。これ て残りの 350 バイトは再びリストに返しま がヒープの中にとられていることを前提と ら三つのプロックは , 双方向リストて、連結 す。しかし , あまりに小さいプロックしか しています。て、すから , ヒープ領域内にな されています。また , 図の上のほうにリス 残らない場合には , 残りの分もいっしょに こて、は , すべてのプ いプロックを渡されたり , ヘッダが壊れて トの頭があります。 割り当てたほうが , プロックの数も減り好 いたりすると致命的なのて、 , マジックナン ロックがアドレスの昇順にリストて連結さ 都合て、す。そこて、 , 要求されたプロックの ーを使ってチェックします。 れています。 大きさとみつかった空きプロックの大きさ メンバ occupied は , そのプロックが使用 このように双方向リストて、連結されてい の差が DELTA 以下だったら , プロックを分 中てあるか否かを示すものてす。 0 てなけれ ると空き領域の併合が簡単にて、きます。 Fi 割せずに少々のムダを承知て、丸ごと割り当 ばそのプロックは使用中 , 0 ならば空いてい g. 4 の状態て , 中央のプロックが解放された ててしまいます。実際問題として , List 1 を としましよう。このとき , 前後のプロック ることを表します。メンバ next と prev は , MS ー DOS マシンて、コンパイルするとヘッダ それぞれ前と後ろのプロックへのポインタ がともに空き状態て、あることが簡単にわか て、す。最後にメンバ body は , プロックの本 の大きさ HEADER SIZE は 10 になりますか ります。なぜなら , 双方向リストて、前後の ら , 10 バイト以下の空き領域は無意味にな 体を表しています。関数 allocate block は , 関係が , メモリ上ての物理的な位置関係を そのまま反映しているからてす。またプロ メンバ body を指すポインタを返します。 ります。 ックの併合もごく簡単にて、きます。 26 行目のマクロ WORD ALIGN は , パラ 11 行目のマクロ HEADER SIZE は , メン メータをワードサイズに切り上げるための バ body を除いたヘッダの大きさを表すマク リ管理のプログラム ものて、す。 29 行目の変数 block chain は , プ ロて、す。 15 行目のマクロ DELTA は , プロッ クの最小の大きさを定義するものて、す。プ ロックリストの頭てす。 32 ~ 33 行目て、定義 List 1 のプログラムをもとに説明していき メモリ管理 List 1 ましよう。このプログラムは三つの関数か ら成っています。関数 initialize memory typedef struct memblock { / * プロックのバイト単位の大きさ ( ヘッダを含む ) * / int size; は , 初期化を行うものて、す。ほかの関数を / * マジックナンバー * / unsigned char magic; unsigned char occupied; / * 1 ならこのプロックは使用中。 0 なら空いている。 * / 使う前に , 必ず 1 回だけ呼び出しておかなけ struct memblock *next; / * 次のプロックへのポインタ * / struct memblock *prev; / * 前のプロックへのポインタ * / ればなりません。関数 allocate block は指定 / * メモリプロックの本体 ( ダミー ) * / char bodyC1] ; した大きさのメモリプロックを割り当てま / * メモリプロックのヘッダの大きさ ( メンバ b y を除く ) 。 * / す。ライプラリ関数の malloc と同じ働きを ー sizeof(charC1])) (sizeof(struct memblock) #define HEADER_SIZE します。関数 free block は , allocate bloc / * 割り当てる大きさと見つかったプロックの大きさの差が DELTA 以下 k て、割り当てたメモリを解放するためもの だったら、プロックを分割せずに丸ごと割り当てる。 * / #define DELTA 10 て、 , ライプラリ関数の free に当たるものて、 / * マジックナンバー。 magic フィールドには常にこの値をセットしておく。 す。 プロックを解放するときに , プロックが allocate ー block で割り当てられたもの であることを確認するのに使う。 * / 1 ~ 8 行目は , プロックのヘッダを表す str #define MEM_MAGIC 0xa5 uct memblock 型の定義てす。メンバ size は / * ワードの大きさ ( バイト数 ) 。 * / ( ヘッダを含む ) プロックの大きさて、す。メ #define WORDSIZE 2 ンバ magic は , プロックを解放するときに渡 / * 大きさ n がワードの大きさ WORDSIZE の倍数になるように切り上げる。 されたプロックが , allocate block によって 1 りれ 0 4 0 6 7 8 9 0 1 人っ 00 4 戸 0 ^ 0 ー 8 0 ) 0 1- り 0 00 4 - -0 1 ↓ 1 亠 1 人 1 よ・ -1 1 人、ー一 11 1 人 14 つつなつ 00 りり 80 C MAGAZINE 1 2 4
ライフボート lnformation from Compiler Makers Q PC ー 9801 シリーズで , DOS / 16 M を使ったツールを RAM ディスク などと共存させる場合に , 拡張メ モリの競合が起こらないように環 境変数を適切に設定する必要があ ります。設定すべき値を取得する いい方法はありませんか。 A MS-DOS についてくる RAM DISK. SYS や EMM. SYS の場合 , これらのデバイスドライバが使用 した残りの拡張メモリの大きさが , 0040 : 0001 番地に 128K バイト単位 て入っています。ただし , 将来の 機種にわたって , このアドレスに 拡張メモリの大きさが格納される ことはメーカによって保証されて いるわけてはありませんのて、 , 新 しい機種や型名が同じても ROM が 変わったりした場合には , 確認が 必要てす。しかし , このことはか なり広く知られているのてあまり 心配はいらないと思われます。ま ハイレゾモードては , メモリ ウインドウ機構により , デフォル との状態ては , 80000h—BFFFFh 番地が , 100000h ~ 13FFFFh 番地 と同じ RAM に割り付けられていま すから , 拡張メモリとしては , 14 0000h 番地から使用するように指定 しなければなりません。ハイレゾ モードとノーマルモードの区別を プログラムて行うには , 0050 : 00 01 番地の第 3 ビットを参照して , そ の値が 1 の場合には , ハイレゾモー ドになります。 List 1 のようなプロ グラムて , 設定値が判断てきると 思います。なお , この部分を DOS / 16M が自動的に行えるように , 現 在作業中てす。 Q stdaux を使って RS ー 232C ポー トを通して , データのやり取りを したいのですが , データをうまく 受け取れません。データの送信は 行えるのですが , stdaux からの読 み込みはできないのですか。 A stdaux は , 読み書きの両方が 可能て、す。 f 叩 en 関数のパラメータ て、いうと , 、、 r 十〃て、オープンされた 状態になります。てすから , ひと つのファイルポインタ stdaux て、 , 読み込みと書き込みを兼用するこ とがてきます。ただし , こて、注 意が必要なことは , 読み込み状態 から , 書き込み状態へ移るとき , または , その逆に書き込み状態か ら読み込み状態へ移るときに , モ ードを切り換えることを知らせる ために , fseek 関数を呼び出さなけ ればならないということてす。し たがって , fprintf 関数などを用い て , stdaux に書き込んだ後 Mfget s 関数を使って stdaux から読み込む 場合には , fgets 関数を使用する前 に , fseek (stdaux, OL, I) を呼び 出さなければなりません (List 2 参 照 ) 。 fseek 関数に渡すパラメータの 意味は , ファイルポインタ stdaux に対して , ファイル内の現在の位 置から相対的に 0 バイト移動 , すな わち移動しないということて , うすることにより副作用を与えず に , f ek 関数を呼び出すことがて きます。 Q マニュアルには , 日本語処理 を行うときに一 e0 〃オプションを指 定するように書かれていますが , この指定をしなくても , 何も異常 が見られません。もちろん , プロ グラム中には , 漢字を表示したり , 日本語文字列を扱う箇所が多数あ ります。なぜでしようか。 A 文字列の中て , \ 記号は特別な 意味を持ちます。たとえば , \ 記号 の後に , n があれば , 改行コード (ASCII コードの 0x0C ) に変換され ます。 MS-DOS 上て、は , 漢字コー ドとしてシフト JIS コードが使われ ますが , シフト JIS コードの第 2 バ イトにはこの \ 記号と同じ値 ( 0x5C ) を持っ漢字があります。たとえば , 、、導クという漢字のシフト JIS コード は , 0X895C てすから , 、、噂〃という 文字列は , 、、 \ X89 \ 〃と同値て、す。 すると , 第 2 バイトの次の引用符 は , 文字列終了の引用符て、はなく , 文字列中に引用符を表示するコー List 1 1 : #include く s i0. h > Lattice C ドになってしまい , 文字列を終了 させる引用符がなくなってしまい ます。 ー e0 オプションの日本語処理と は , 文字列中の漢字コードを正し く認識し , 漢字の第 2 バイトが \ 記 号と同じコードてあっても , \ 記号 の処理をしないようにするオプシ ョンて、す。て、すから , 、、クという 文字列を一 e0 オプションなして , コ ンパイルした場合には , コンパイ ルエラー ( 文字列が閉じていない ) になりますが , ー e0 オプションをつ ければ , 正しくコンパイルてきま す。また , 偶然にも第 2 バイトに \ 記 号と同じコードを含んだ漢字コー ドを使っていなかった場合には , ー e0 オプションをつけても , つけな くても , 正しくコンパイルて、きま す。 2 : unsigned char far * DATA401 = ( si 馴 char far * ) 0X00400001 : 3 : unsigned char far 事 DATA501 = ( 凹 si 馴 char far * ) 0X00500001 : 4 : 皿ⅲ ( ) 6 : if(*DATA501 & 0X08 ) ( / * High resolution * / printf("set DOS16M = 1 280 ー " , *DATA401 * 128 + 1024 ) : 9 : else ( / * NormaI * / printf("set DOS16M = 1 01 \ n " , *DATA401 * 128 ) ; 11 : 10 : 8 : 7 : List 2 lnformation from Compiler Makers 161 17 : fprintf(stdaux, " : 16 : fseek(stdaux, 0 し 1 ) : 15 : printf("Received ね : buff) : 14 : fgets(buff, 80 , stdaux) : 13 : fseek(stdaux, 0 し 1 ) : 12 : fprintf(stdaux, Require reply"); 11 : fprintf(stdaux, "Transfer parameter") : 10 : fprintf(stdaux, "Transfer commnad") : 9 : system( ” S般記「 0 1200 b8 none sl xon ” ) : 8 : memset(buff, 0 , 8 の ; 6 : char 面 ff [ 8 の : 4 : main() 3 : #include く string. h> 2 : #include く stdlib. h> 1 : #include く s i0. h 〉 7 :
せばよい (offsetof は size t 型の値を返す ) 。 #include く stddef. h > struct example { Char unsigned long Size t int fname S lZe status ; [ 64 ] ; i=offsetof(struct example, status) ・ 型をパラメータとして渡すことから , off setof は通常の関数として実装することはて、 きない。大多数の処理系て、は List 1 のような 定義のマクロになっているものと思われる。 ただし , なかにはどうしても 0 をキャスト して望みの構造体へのポインタと見なせる 値を作り出すことは難しいという処理系も あり , そのような場合にはコンパイラが組 み込み機能として処理するしかないだろう と ANSI の Rationale( 理由書 ) は述べてい 演算子ツ″とー > ″の優先順位 &x. fname [ 0 ] は & (). fname [ 0 ] ) とい う意味て、ある。ヾ . 〃や添字づけ ] 〃 ( 添字づ けも一種の演算子と考えることがて、きる ) な どは , 単項の、、 & クよりも結合力が強く , 優先 される。そのため , この場合にはカッコは 不要て、ある。、や、、 [ ] 〃は単項演算子よりも まだ高い優先順位を持っていて , 実のとこ ろもっとも優先順位の高い演算子て、ある。 それて、は , x. fname [ 0 ] において、、 . 〃と、、 [ ] 〃 はどちらが優先されているのだろうか。 Fig. 1 postfix-expression, p 「 imary-expression の構文定義 postfix—expression . prmary—expresslon postfix¯expression [ expression ] 十十 ー > identifier ( argument-expression 0 が ) Table 1 ( expression ) string-literal constant identifier pnmary¯expresslon . postfix—exression postfix—exressi 0 n postfix—ex 「 essiO n postfix—exresslon . identifier postfix¯exression プレ形式 ボスト形式 ( 十十 p)—>m p 十十—>m ポインタへの加減算 メンバへの加減算 p—>m 十十 十十 p—>m more p 十十一 > m = q ー った 作は ( 少なくとも公式には ) 許されていなか 許されるのて、ある。 K & R の時代にはこの操 ひとって、あるとすると , 次のような表現が ポインタて、あり , m がその構造体のメンバの n になることて、ある。 p および q が構造体への ーを適用した式もまた postfix-expressio こて、注目すべきことは , 後置の十十や の名の由来だと思われる ) 。 ら右」へと結合するのて、ある ( それが postfix 後置式のカテゴリに登場する演算子は「左か が優先される。あるいは表現を変えれば , 許される。ということて、 , 結論としては、、 . ression なのて、 , その後に添字をつけるのは dentifier という式全体は , また postfix-exp になっている。ただし postfix-expression. i 含まれるメンバ名て、なければならない されない。実際には左辺が表す構造体型に よび、、一 >〃の後には idetifier( 識別子 ) しか許 この構文定義から明らかなように ことがわかる。 子力℃て、もっとも結合力の強い演算子て、ある ないため , これら postfix-expression の演算 3.1 ) には , カッコを除けば演算子は登場し とがわかる。 primay-expression (ANSI 3. および後置のデクリメント、、 ー〃て、あるこ 選択、、一 > 〃 , 後置のインクリメント、、十十〃 , 呼び出し、、 ( ) 〃 , メンバ選択、、 . ク , 間接メンバ の類としては , 配列の添字づけ、、 [ ] 〃 , 関数 postfix-expression に含まれている演算子 3.3.2 , Fig. 1 ) 。 という構文カテゴリに属している (ANSI れらはどちらも postfix-expression ( 後置式 ) ー > 〃というのは , 実際に ANSI C ー more 117 のを切り出すように構成されているからて、 部は , トークンとして解釈可能な最長のも が保証されている。コンパイラの字句解析 に分解されて期待どおりに解釈されること 〃とー > 〃というふたつのトークン は避けるべき表記て、はあるが , 一応文法上