23 ~ 25 行目のトークンは演算子を表すも のて、す。演算子には + など 1 文字のもの と , + + などの 2 文字のものがありますが , yacc の制限て、 , 文字定数型の定数は 1 文字の ものしか許されていません。て、すから , + + や & & などのトークンは識別子を使って表現 します。また , 同じ優先順位をもつ演算子 が複数個ある場合 , ひとつのトークンにま とめてしまうほうが , パーサが小さくなり ます。 たとえば , 2 項演算子 + とーは同じ優先順 位て、すが , これらをまとめて addop として 扱えば , もともとふたつの規則 expr addop expr になるところを , ひとつの規則 expr ー expr ' expr ' 十 ' expr expr expr 74 CMAGAZINE 1989 12 優先順位を宣言しておく必要はありません。 かの演算子とは別扱いをするのて、 , ここて、 すが , 実をいうとコンマ演算子だけは , ほ のちほど文法の定義のところて、説明しま て、 , %nonassoc として定義します。 た , + + とーーは結合することがて、きないの 子 ( ? , : ) , 単項演算子は右結合て、す。ま 子は左結合て、すが , 代入演算子 , 条件演算 階の優先順位があります。ほとんどの演算 します 0Cm の場合 , C と同様に全部て、 15 段 順位の低いものから高いものヘ順番に定義 44 ~ 58 行目が優先順位の指定て、す。優先 優先順位 の対応表を表 3 に示します。 バ OP として参照します。トークンと演算子 参照します。この値は char 型をもち , メン が yylval. op にセットして , アクション中て、 す。具体的には , レキシカルアナライザ yylex いう情報は , トークンの値としてもたせま さくなります。 + て、あるか , ーて、あるか , と ( = 規則の数 ) が小さくなると , パーサも小 て、すませられます。このように文法の規模 凵 ST ・ 2 143 : 144 : 145 : 146 : 147 : 148 : 149 : 150 : 151 : 152 : 153 : 154 : 155 : 156 : 157 : 158 : 159 : 160 : 161 : 162 : typ e_ name abst dec I arator ・ abst_decl arator2 : / * * * * statement 0 : type-spec abst-dec larator type-spec ' * ' abst_dec 1 arator abst_dec larator2 ' ( ' abst_decl arator rp abst-dec Iarator2 ' [ ' expr ' ] ' abst dec larator2 ' ( ' rp abst declarator2 ' ( ' param_l ist rp ' ( ' param-list rp 163 : 164 : 165 : 166 : 167 : 168 : 169 : 170 : 171 : 172 : 173 : 174 : 175 : 176 : 177 : 178 : 179 : 180 : 181 : 182 : 183 : 184 : 185 : 186 : 187 : 188 : 189 : 190 : 191 : 192 : 193 : 194 : 195 : 196 : 197 : 198 : 199 : 200 : 201 : 202 : 203 : 204 : 205 : 206 : 207 : 208 : 209 : 210 : 211 : 212 : 213 : 214 : 215 : 216 : stmt list stmt i f_head whi le_head for head expr_opt sw i tch head : / * empty * / stmt_l ist stmt { yyerrok stmt 1 ist error compound stmt expr SC if head stmt : SWITCH ' (' expr rp expr : / * empty * / FOR error : FOR ・ ( ' expr_opt sc expr_opt sc expr_opt rp WHILE error : WHI し E '(' expr rp IF error IF ' ( ' expr rp ' stmt ー IDENTIFIER ー GOTO IDENTIFIER sc RETURN expr sc RETURN sc CONTINUE sc BREAK sc ' stmt ー DEFAU し T stmt CASE expr sw itch_head stmt for head stmt stmt WHILE ・ ( ' expr rp sc wh ile head stmt stmt i f_head stmt ELSE
プログラムを制御する制御文 匚 , = コ 五ロ はじめて学ぶ 0 プログラミング 第 3 回高木聡 / 山崎信行 前号では , C 言語の「変数 , 定数と変数宣言」の基礎について説明し ました。変数や定数か扱えるようになってはじめて , C 言語による プログラム作法の第一歩を踏み出したことになるわけです。しかし 変数を使えるようになっただけではプログラムを書くことはできま せん。今回はプログラムを書くうえで不可欠な , プログラムを制御 する「制御文」について詳しくお話しましよう。 ひとつの文として扱われます。 C 言語におい 左辺が右辺より大きいとき真 て制御文はその次にくる 1 文のみを制御し 左辺が右辺以上のとき真 ます。したがって , 複数の文を一度に制御 するときには , 複文を用います ( 以下 , 制御 等値演算子 文の書式を記述するときに , 複文の書き方 左辺と右辺が等しいとき真 プログラムを制御するということは , あ ! = 左辺と右辺が等しくないとき真 る条件をもとにして , 文の実行を判断した は省略します ) 。 り , 繰り返しを行い、、プログラムの流れク 書式 if 文は , これらの演算子を使った①式の if( 式 ) { を変えていくことて、す。具体的には変数を 演算結果 ( 真偽の判定 = 式の値 ) により処 用い , その変数の値によって条件分岐した 文 1 ; 理の流れを判定しますが , 実際には , 式の り , 繰り返したりするのて、す。今回のテー 文 2 ; 値 ( 真偽値 ) は , 次に示すように数値とし マは , 条件分岐するもととなるプログラム て処理され判断されるのて、す。 の幹に相当する部分を解説しましよう。 文 n ; 偽ーー→数値 0 if 文の制御構造と比較演算子 if 文の書式 Fig. 1 に if 文の制御構造を示しました。 Fig. 1 て、わかるとおり if 文て、はまず①式を判 断し , 真ならば②を実行し , 偽ならば②は if 文は , ある条件をもとに次の文を実行 無視され実行されません。①式を判断する するかどうかを決定します。 if 文の書式は ときには , 普通は比較が行われます。 次のようになります。 比較 ( 演算 ) には , 関係演算子と等値演算 書式 子が使用されます。ここて、関係演算子と等 if( 式 ) 値演算子を挙げておきましよう。 文 ; また , 制御したい文が 2 文以上ある場合 関係演算子 は , その部分を { } て、囲みます。 { } て、囲ま く左辺が右辺より小さいとき真 れた文は , 複文と呼ばれ , ひとかたまりて く = 左辺が右辺以下のとき真 Fig. 1 if 文の制御構造 (a) 1 文制御 if ( ① ) (b) 複文制御 if ( ① ) ー NO ①が真 ? YES ②の実行 はじめて学ぶ C プログラミング 113
語に依存しますが , 文の区切り記号 , 閉じ カッコなどを採用します。 Cm パーサて、は , 今説明した ' } ' ' ; ' が「つつかかり」とな るわけて、す。 式の定義 式の定義は List2 の 229 から 280 行目まてて、 す。 nc expr, expr, primary, expr list の 4 つのノンターミナルが定義されています が , このうち , 実際に式を表す『メインエ ントリ』は , expr て、す ( ノンターミナルと は , 形式言語理論の用語て、 , yacc て、は文法 規則の左辺にあたります ) 。残りの 3 つは , expr を定義するための補助的なものて、す。 先ほど簡単に触れましたが , いちばん弱 い ( 優先順位が低い ) コンマ演算子を扱うた めに , nc_expr というノンターミナルを導 入しています。 nc expr とは , non-comma expression のつもりて、名づけたものて、 , コ ンマより強い演算子を含む式を定義してい ます。そして , expr て、は nc expr を使っ てコンマ式を定義しています。 こて、「コンマ式 (comma expression) 」と いう呼び方をしましたが , これはコンマ演 算子て、結合された式のことて、す。ただし , コンマがいちばん弱い演算子て、なければな りません。たとえば , 「 a = 2 , b = 5 」はコン マ式て、すが , 「 a = ( 2 , b = 5 ) 」はコンマ式て、 はありません。なぜなら , コンマはカッコ の中に現れるのて、 , この式て、のいちばん弱 い演算子て、はないからて、す。 一般に , ある演算子がもっとも弱い演 算子として現れる式を降式」と呼びます。 四則演算程度て、すと , 「式」 , 「項」 , 「因子」 と優先順位ごとにユニークな名前をつけら れますが , C のように優先順位が多くなると そうもいきません。そこて、 , このように機 械的に名前をつけるのて、す。また , C て、はい ちばん弱い演算子はコンマなのて、 , コンマ式 = ( いわゆる ) 式 となります。この規則に従うと「 a = ( 2 , b= 5 ) 」は代入式て、あることになります。また , 76 CMAGAZINE 1989 12 non ー comma expression も , 正式には assignment expression ( 代入式 ) と呼ぶべ きて、しよう。 なぜ Cm ( と C ) て、はコンマ演算子だけを特 別扱いしなければならないのて、しようか ? これは , C の文法て、はコンマ式が使えない場 所があるからなのて、す ()m についても同 様 ) 。関数呼び出しのパラメータと初期化式 がそれて、す ( Cm て、は , 初期化はて、きません が ) 。たとえば , は , 正しい解釈て、は「 a 」 , 「 b 」というふたつの パラメータをもっ関数呼び出して、す。しか し , かりにパラメータにコンマ式を書く とがて、きるとすると , これは「 a , b 」という ひとつのコンマ式をパラメータとする関数 呼び出して、ある , と解釈されてしまいます。 初期化式についても同様なことがいえます。 たとえば , int a = 2 , b = 5 ; という宣言て、は , 初期化式としてコンマ式 を許した場合 , a という変数を「 2 , b = 5 」と いう値て、初期化することになってしまいま す。 これは , 区切りと演算子に , コンマとい う同じ記号を割り当ててしまったからて、す。 この衝突を避けるために , パラメータと初 期化式て、は , コンマ式を書けないことにし ているのて、す。ちなみに , どうしてもコン マ演算子を使いたい場合は , カッコて、くく ります。つまり とすれば , 「 (), b) 」というひとつのパラメー タをもった関数呼び出しと解釈されますし , int a = ( 2 , b = 5 ) ; とすれば , 変数 a を宣言してから「 ( 2 , b = 5 ) 」という値て、初期化することになります。 カッコて、くくることによって , コンマ演算 子が外から直接見えなくなり , コンマ式て、 はなくなるからて、す。 expr ( 255 ~ 261 行 ) は , 今説明したように nc expr をひとつ以上 , 間にコンマをはさ んて、並べたものて、す。前回 , 文脈自由文法 の説明のところて、紹介した繰り返しの定石 を使って定義します。また , yacc を使う場 合には , 適当なところに error を含む規則 を入れてエラーリカバリをしなければなり ません。 工ラーリカバリ用のアクションは , Fig. 1 のようにするのが定石て、す。しかし , Fig. 1 のとおりにすれば必すうまくいく , という わけて、はありません。トライ & 工ラーを繰 り返して , 実際のエラーて、適当なリカバリ が行われることを確認する必要があります。 また , 正常なケースについて矛盾なしに文 法が定義て、きていても , error トークンを 含む規則を追加すると , 文法がおかしいと yacc に文句をいわれることもあります。 ういった場合には , よく文法を吟味して処 置を講じますが , そのためには文脈自由文 法の知識が必要て、す。 expr の定義も定石に くらべて少し異なっていますが , これもト ライ & 工ラーをしたためて、す。ェラーリカ バリは , 実用的なコンパイラを作成するう えて、大切なポイントて、あり , コンパイラの 使いこちを左右します。しかし , 本論か ら外れることて、すのて、 , あまり追及しすぎ ても不毛て、しようから , この程度にとどめ ましよう。工ラーリカバリの方法を含め , yacc を使った文法の設計については参考文 献 [ 1 ] をお勧めします。 こて、話が前後しますが , yacc が指摘す るエラーについて説明しましよう。 yacc 使用時のエラーは , 入力ファイルの 文法工ラーと , 定義した文法自身のエラー の 2 種類に大別て、きます ( たいへんまぎらし くて申し訳ありません ) 。前者は yacc ソー スプログラムの文法工ラーて、 , たとえばセ ミコロンを忘れた , などのエラーて、す。 種のエラーは本質とは関係ありませんのて、 , これ以上説明しません。一方 , 後者は入力 した文法自体に矛盾があり , パーサを生成 て、きない , というエラーて、 , 「 shift/reduce conflict 」と「 reduce/reduce conflict 」 という 2 種類のものがあります。 yacc は , パースの過程て、パーサがとりう
三田典玄の ことをやることになる。 つまり , ポインタ変数を使って配列は実 現されているわけだ。ポインタ変数と唯一 違うのは , そのポインタ変数の指す領域が 宣言されたすぐにはもう確保されていて , いって、も使える状態にある , ということだ 実際 , コンパイラの中て、は配列の扱いは すべてポインタ変数として扱われることに なっている。だから , 弊害が生じること・も ある。 つまり , int a [ 100 ] ・ として宣言して , 100 個分の int 型変数の領域 を確保しておいても , コンパイラはその領 域そのものについてはまったく無頓着なの などという「バカ」をやっても , のに / ) ( 100 まて、しか宣言していない = 使えない a C500]=3 ; て、 , たとえば コンノヾイ ラはそれを正しい文法だと思って通してし・ まうのて、ある。もっとも , 最近のコンパイ ラは , けっこう賢くなっていて , この場合 のようにひとつのコンパイル単位内て、 ( つま りひとつのソースコード中て、 ) こういった宣 言と「バカな」使い方をしており , かっ配列 : 要素を表す数がこういった定数て、あれば , 一応 Warning(T 告 ) は出してくれるように なっている。 そういえば昔の BSD/UNIX のコンパイラ て、は , 構造体のメンバー名が違っていた場・現があるために 合なんかて、はエラーて、はなくて「 Warning 」・ が出てくる , という「仕様」もあったから , このくらいはどうて、もいいことかもしれな : ◇ポインタ変数の表現の問題点 , 配列の要素数は通常 int て、表され : る。つまり , 配列要素の何番目かを表す数 : 実践 C プログラマ 養成講座 には int を使わなければならない。そして , いよいよこれをポインタ変数表現とすりあ わせてみることにしよう。 とりあえす , 表現上は int a CIOO] ・ と , 宣言した場合 , その n 番目の要素の参照 には , たとえば という表現を使う。しかし , いいかげんな 三性格だった ( かどうかは私もよく知らないが ) 若き日のカーニハン先生は , 「これはポイン タ変数と同じものじゃないか。だったら同 じように使えるようにしよう」という , 寛大 な配慮を行ったのだった ( と思う ) 。 そこて、 , 前記の表現を というようにも書けて , ェラーが出ないよ うにしてしまったのて、ある。つまり , ポイ ンタ変数の演算がて、きる , という無謀この ことがて、きるようにしたばかりか , うえない それを配列変数用のポインタ変数にまて、使 えるようにしてしまったのて、ある。 この表現は , 「ポインタ変数 a の示すアド レスから n 番目隣のアドレスにある数を変数 三 c に代入しろ」という表現て、あって , 少なく とも「言語表現」というレベルて、は「配列」な どという IBM 的観念はまったくないといっ てよい コンパイラの内部に立ち入ることがて、き るからこそ表現しえた , というとかっこい いが , これははっきりいって言語表現レベ ルの混同以外の何者て、もない。が , この表 けっこう現実のプログラ ムて、は役に立っこともしばしばて、ある。コ ンヒ。ュータやそれを動かすために考えられ た言語というものが , じつはまだ開発途上 三のものて、 , 不完全なものを扱っているのだ から , 使う言語のほうも柔軟性がなければ 三ならない , という「研究者特有の謙虚な姿勢」 とも受け取れるが , 「俺たちはコンパイラを つくってんだから , コンパイラのソースコ ードがあるんだぜ , いいだろー / 」というよ うな「研究者独特の奢り」にもとれることて、 はある。 とにかくそういう表現を許してしまった : C 言語て、ある。許されると後はまっしぐらに 堕落の道を歩む , というコースをたどるの : が私たち凡夫の常て、ある。て、 , はまってし まったものはしかたがない , と腹をくくっ : て , 八百屋お七よろしくさらに先に突き進 むことにする ( なんか話が違ってきたかな ◇ポインタ変数演算の意味と留意点 て、 , ポインタどうしの演算だがここに変 数の型というもうひとつのファクタが初め : て出てくる。 まず , 前記の表現て、は配列要素の番号を : 表す n も , ポインタ変数演算て、加算されてい る n も int 型て、なければならない。なぜかとい うと , 両方とも n は何バイト目とか何アドレ ス目という直接のアドレスを演算する数な のて、はなく , n 個お隣という意味だからて、あ る。つまり , は , 「配列名 a の配列の第 n 番目の要素」とい う意味だし , a 十 n ; は「ポインタ変数 a の n 個分隣のポインタ値」 という意味だし , それに「 * 」を付けた という表現は「ポインタ変数 a の n 個分隣のポ インタ値の示すところにある値」ということ ということは int 型が 16 ビット長て、あれば a + n というポインタ値は , 実際のコンピュ ータのアドレス ( 物理アドレス ) ていうと a か らは n * 2 バイト離れたところを指すことに なる。同様に int が 32 ビットならば n * 4 バイ ト離れたところを指すことになるのてある。 しかし , 実際のプログラムを書くにあたっ 実践 c プログラマ養成講座 97
if ー else 文は , if 文と同様に条件判断をす る制御文て、す。 if 文との違いは , 条件式の 値が真のときだけ実行される文と , 偽のと きだけ実行される文があるということて、す。 書式を以下に示します。 書式 if ( 式 ) 文 1 ; else 文 2 ; Fig. 2 が if-else 文の制御構造て、す。①式 が真のときは②のみが実行され , 偽のとき は③のみが実行されます。条件式が真か偽 かによって , ②か③のどちらか一方だけが 真一一一以外の数値 のほうが等値演算子よりも優先順位が高く 実行されるわけて、す。 て、すから if 文は ( ) の中の値 , つまり なっています。優先順位などについては演 ①式の値 , が 0 ( 偽 ) かそれ以外か ( 真 ) か if-else 文の例 算子のところて、説明します。 を調べ , 次の処理を決めていくのて、す。 if 文の例 のことを利用して , たとえば①式のところ List2 を見てください。これは , うるう年 に 1 を書けばその if 文をつねに真とするこ を判定するプログラムて、す。うるう年の条 ともて、きるのて、す。 List1 を見てください このプログラム 件は西暦が 4 て割り切れてかっ 100 て、割り切 ところて、 , 関係演算子と等値演算子は同 は , ふたつの変数を読み込み , その大小を れないて、 , なおかっ 400 て、割り切れるという 比較するものて、す。まず int 型の変数 x , y じような働きをするのにふたつに分かれて ことて、す。プログラムて、は , int 型の変数 x いるのはどうしてなのか不思議に思われる を旦言して , それぞれの変数に値を読み込 を宣言し , 西暦を代入したあと if-else 文て、 んだあと , if 文て大小比較をして出力する かもしれません。これはふたつの演算子の 変数 x がうるう年かどうかを判断していま 優先順位が違うため , 別々の名前がつけら ものて、す。 す。 れているのて、す。具体的には , 関係演算子 条件式中の演算子 ' は割り算の余りを求 めるものて、す。たとえば a, b, c, を整数と して c = a%b とすると a/b の余りが変数 c に代入されます。また , 条件文の中に ' & & ' という記号が入っています。これは , 式の 値どうしの論理積 AND をとることを意味し ています。つまり if 文のカッコの中て、は , List 1 十レ & 十》 & 十レー十しー CO + レ a. 0. 、 1 りなっ 0 -4 ・ - -0 れ 0 ー 8 0 》 0 , 1 り 0 っ 0 -4 ・ -. 0 れ 0 ー 8 0 = %d の方が Y ニ %d よりも大きいです。 Yn" = %d と Y ニ %d は等しいです。 Yn ” if ( x く y ) pr i nt f ( ” X = %d の方が Y = %d よりも小さいです。 \ n" Fig. 2 if ー else 文の制御構造 (a) 1 文御制 ( b ) 複文制御 if- else 文の書式と制御構造 List 2 1 : # i nc 1 ud e く std i 0. h > 2 : 3 : vo i d ma i n ( ) 5 : int year; 6 : pr i ntf ( " 西暦を入力して下さい。 7 : scanf("%d ” &year ) : 9 : if ( ( year % 4 = 0 & & year % 100 ! = 0 ) Ⅱ year % 400 = printf(" 西暦 %d 年はうるう年です。 Yn" year ) : e 1 S e printf(" 西暦 %d 年はうるう年ではありません。 yn" NO ①が真 ? YES の実行 ③の実行 year ) : 114 CMAGAZINE 1989 12
三田典玄の 実践 C プログラマ 養成講座 : 題はかなり大きな問題を抱えている部分て、 ある。が , 一方て、コンピュータそのものが 三「あまり完成されていないキカイ」て、あると 三いう見方に立てば , 「これも仕方ないかな」 という部分てもある。数学屋さんは理論的 な完璧性を求めるのだろうが , コンヒ。ュー 三タの基になっている半導体はいちばん不確 三実な世界て、ある ( 数学にあくまて、こだわれば ) 三「確率」の世界て、動いているものてある。私 三たちは自然の偶然によって生まれてきたも 三のて、あり , コンヒ。ュータも自然の偶然が動 三かしてくれているものなのだ , という考え 三方もなくはない て、あれば「とにかく使えればええんて、ない の ? 」という地平に立つコンピューティング てはこういった型を気にするプログラムは・ 2 . ポインタ変数の演算はつねに「ポイ というのもあってもよいのて、はないだろう ンタ変数」と「整数 ( int 型 ) 」の演算と あまりおおっぴらに書いてはいけない 三か ( と , 最後にはカーニハン先生の肩をもっ 「書けるんだからいいじゃないかよぉ / 」三 しなければならない 三わけて、す ) 。 3 . ポインタ変数の演算て、演算に供され・ とすごまれると私も返す言葉がないが , そ・ る整数は「 ~ 個隣」という意味て、ある。 うすることは移植性を犧牲にすることに直 : て、 , 次回はヨタ話抜きて、構造体とポイン につながる。 三タ変数の話にしようと思う。 C 言語はよく移植性のよいプログラムが書三 ポインタ変数と配列の関係を深く追求し ていくまて、もなく , C 言語て、の言語としての : ける , というが , あくまて、「書ける」という・ こて、突き当たるのだが , : 「いい加減さ」に のて、あって , 「書く気持があり , かっ書く技 : 量があって」「書ける」のて、ある。気もちも技三 とくにこのポインタ変数と配列の混同の間・ 量もない という方には諦めていただくほ : かない どちらも持とうと思ったらなんと : かなるものて、あるのて、 , 今「ない」からとい って諦める必要はない ただ実際の現場 : 0 、、 てプログラムを書くのは止めてほしいと私三 は思う。なにせそんなプログラムつくられ : たらデバッグするのは「わかる人」て、ある。 はた迷惑このうえない。 さて , 今回の結論はこうてある。 1 . 配列変数はポインタ変数と本質が同 : じものなのて、 , 配列て、宣言された変 : 数をポインタ変数を使って表現てき : - 5 回 C ニ a 〔 n おト、 C=*Ca + n); ハ等シ総 .. 530 530 番地 ・ー - ー -- -- ー - ・ i n t が 100 個 の領域 98 CMAGAZINE 1989 12
C の直言・書き方と読み方 学 本ロ 0 五ロ 乗松保智 前回はがどのようにメモリ上に配置されるかをしました。鉅は や関数の館について考えてみましよう。 C の韮 int i : のように簡戦よものならわかりやすくていいのですか , のように複雑になると , 亠オは困難になります。そこで , 鉅は C の 館の方を考えます。的にできないのなら , してすれば いいのです。 まずいろいろナについて月し , 続いて C 讎訒館のを月しま す。「よくわからんそー」という声カ墹こえてきそうですが , まあ , なんとなく そうかなあくらいにしていただければです。取の言謎には yacc の を使います。 yacc につい召ー載の「 yacc による C コンヾイラブロ グラミング」をしてくださし最後に亟をわかりやすく書き直訪法とそ の方を月します。 C て、は基本的な算術型として , 次のものが 8 ビット Char 用意されています。 16 ビット short C の型について int short 32 ビット char int unsigned signed long 32 ビット long VOid float double まずは型についてざっと復習しましよう。 となっていることが多いようて、す。 こて、はハードウェアによって直接処理す 列挙型 また , signed, unsigned という , 変数 ることがて、きる基本型と , すて、に存在する 列挙型は がとる値を指定するものもあります。 signed 型を修飾してつくられる派生型に分けて考 enum { BLACK, WH 工 TE } : を指定すると「符号付き」の数となり , un えます。 C の基本的な型といえば , すぐに思 のように宣言される記号定数の一種て、 , 内 signed だと「符号なし」になります。 8 ビッ いつくのは int , char, float などて、しよう。 部的には int て、表現されます。そのため , 基 トの数値が符号付きの場合 , その値は一 128 これらは「基本的な算術型」と呼ばれ , ハー 本的な型に含めてあります。 から十 127 まて、となります。符号なしの場合 ドウェアが直接サポートて、きます。また , また , C の文法は unsigned int のように には 0 から 255 まて、て、す。これらの型はハー これらの型の大きさ ( ビット長 ) は各コンパ これらを自由に組み合わせることがて、きる ドウェアが直接サポートされますが , float , イラ , 実行するハードウェアによって , 異 ようになっています。ただし , char int の double, long double といった浮動小数 なっていてもいいことになっています。 ようなわけのわからない組み合わせは意味 点型のデータの演算は , ソフトウェアて、ェ たとえば , 多くの 16 ビットマシン上て、は 的に禁止されています。 ミュレートされることもあります。 void は特別な意味をもっ型て、 , 関数が値 8086 や , 68000 などのマイクロプロセッサ を返さないことやパラメータをとらない は , CPU 本体だけて、は浮動小数点数の演算 とを示すとき , ポインタがどんな型を指し はて、きません。ハードウェアて、演算を実行 てもいいことを示すときに使われます。 するためには「コプロセッサ」が必要て、す。 浮動小数点演算用のコプロセッサを搭載し 派生型 となっていますが , ていないシステムては , ランタイムライプ ションな ワークステー 派生型というのは「すてに定義された型を どの 32 ビットマシンて、は ラリの中に用意されている浮動小数点演算 使って新しい型をつくる」という意味て , 基 サプルーチンが呼び出されます。 C 言語雑学講座 123 16 ビット 16 ビット 32 ビット Char short int long
五ロ はじめて学ぶプログラ ミンク 匚二コ コラム 1 論理積 ' & & " と論理和 ' 凵 ' 条件式を , ふたつの条件を満たしているときのみ真にするときはどうすればよいのでしよ うか。あるいは , ひとつの条件が成り立っているか , もうひとつの条件が成り立っている場 合に , 条件式を真にしたいときはどうするのでしようか。このようなときは , それぞれ論理 積 ' & & ' と論理和ドをとります。これらは , 論理演算子と呼ばれ , 左から右に評価されま す。たとえば , 変数 x が int 型だとします。 printf("%d", x); この if 文では , 変数 x が 0 以上 9 以下のときだけ , 関数 printf が実行されます。論理演 算子は , 算術演算子よりも優先順位が低いので , 上の if 文は以下のようにカッコを省いて書 けます。 Fig. 3 else - if 文の制御構造 (a) 1 文制御 ( b ) 復文制御 if ( ① ) ー ④ : ⑤ : if ( ① ) ー ④ } else if ( ③ else if ( ③ ) else YES ②の実行 ①が真 ? NO ③が真 ? NO. if(x>=O&&x く = 9 ) printf ("%d", x); しかし , 慣れないうちはカッコをつけたほうがわかりやすいので , カッコはつけたほうが よいでしよう。 また , 論理積 ' & & ' のほうが論理和ドよりも優先順位が高くなっています。たとえば , 以下の例を見てください。 if (x 〉 = 0 & & x く = 9 ーー x 〉 = 100 & & x く = 999 ) printf ("%d", x); これは , 0 以上 9 以下 , または , 0 以上 999 以下のときに関数 printf が実行されます。カ ッコを使うと次のように書けます。 if ( (x 〉 = 0 & & x く = 9 ) 凵 (x 〉 = 100 & & x く 999 ) ) printf ("%d", x); 最初の例はバッと見ただけではよくわかりません。しかし , カッコを使うと読みやすくな るのがわかるかと思います。すべての演算子の優先順位を覚えても , プログラムが読みやす くなるようにカッコを使うことをおすすめします。また , 式の判断は左カら右に行われるの で , 複数 0 式を ' & 0 ' や ' 凵 ' 結ぶときは , 重要な判断を先 0 = 置くほうカ : 効率 0 よ 0 、プ 0 グ ラムとなります。 YES ④の実行 ⑤の実行 0 「 判断 ? ①が真 それ以外 ③か真 ⑤の実行 ④の実行 ②の実行 式ともに偽のときだけ⑤が実行されます。 else-if 文は , ある条件をもとに条件分岐 (x%4= = O ) かっ ( x % 100 !=O) を判断してい このように else-if 文は , ある条件をもとに するときなどに用います。この条件文も if るのて、す ( コラム 1 参照 ) 。 多方向に分岐するのに適しています。また , 文のバリエーションのひとって、す。 else ー if もし条件が真なら , 11 行目の printf 文を else if ( 式 ) の部分は何回て、も繰り返して書 文の書式は以下のようになります。 実行し , if ー else 文から抜け出てプログラム は終了します。もし条件が偽なら , 13 行目 けます。 書式 if( 式① ) の printf 文を実行し同様にプログラムは終 else-if 文の例 文 1 ; 了します。 else if ( 式② ) List3 は 1 文字を入力して , それが大文 文 2 ; 字 , 小文字 , 数字 , あるいはそれ以外て、あ るかを判断するプログラムて、す。 5 行目て、 int 文 3 ; 型の変数 ch を宣言し , 8 行目て、 scanf 関数 Fig. 3 が else-if 文の制御構造て、す。①式 を用いて 1 文字入力したあと , 10 行目から 17 行目にかけて else ー if 文を用いて判断をし が真のときは②のみが実行され , ③式が真 ています。この場合 , scanf 関数て、文字入 のときは④のみが実行されます。①式 , ③ はじめて学ふ C プログラミング 115 else- if 文の書式と制御構造 else
コンヾイラ の内部を詳解 しましよう。 30 ~ 40 行目は , セマンティッ クスタックに積むデータの型の定義て、す。 トークンの定義 List2 のうち , はじめのほうにある %token て、始まる行がトークンの定義て、す ( 15 ~ 25 行 目 ) 。 IDENTIFIER は識別子 , CON STANT は定数を表します。トークン 工 DENTIFIER は char * 型の値をもち , スタック上て、はメンノヾ symbol として参照 されます ( 7 , 15 行目 ) 。レキシカルアナライ ザは , 識別子名を表す文字列へのポインタ を yylval. symbol にセットします。定数に は , 数字 , 文字定数 ( ' a ' など ) , 文字列 ("hello world \ 心など ) の 3 種類がありますが , これ らを一括して CONSTANT て、表現します。 CONSTANT の値は , const 型て、表現され ます ( 4 , 16 行目 ) 。 17 ~ 18 行目 ( 工 F から E X TERN まて、 ) は , Cm のキーワード ( 予約語 ) て、す。レキシカルアナライザがこれらのキ ーワードを認識すると , 対応するトークン に変換します。 20 ~ 21 行目は区切り記号て、 , それぞれアポストロフィ ( ' ) て、はさんだも のがトークンになります。 [ 表 3 ] トークン名と演算子の対応 ( 文字定数型のものは除く ) 演算子トークンの値 トークン名 addop 十 shiftop mulop COmPOP 十十 0 凵 ST ・ 2 error func_body fi le error fi le error func_body 69 : 70 : 72 : 73 : 77 : 78 : 79 : 80 : 82 : 83 : 84 : 85 : 86 : 87 : 88 : 89 : 90 : 92 : 93 : 94 : 95 : 96 : 97 : 98 : 99 : 100 : 101 : / * * * * declaration 102 : 103 : 104 : dec larat i on ・ tYPe-spec declarator list sc 105 : type-spec sc 106 : 107 : 108 : 109 : 1 10 : 114 : 116 : 1 17 : dec larator 1 18 : 120 : 121 : 122 : 123 : 124 : 125 : 126 : 127 : 128 : 129 : 130 : 131 : 1 i s t param 132 : 133 : 134 : 135 : 136 : 137 : 138 : 139 : 140 : 141 : 142 : : func def EXTERN func_def declaration EXTERN declaration extern def func def ・ type_spec dec larator func_body dec larator func-body ・ { ・ dec laration_l ist stmt-list ' } ' func-body dec laration_l i st: / * empty * / declaration_l ist declaration { yyerrok; } declaration_l iSt error type-spec dec 1 arator 1 i st : dec larator declarator I ist dec 1 arator ・ * を dec larator declarator2 十一Ä\,/äQXvVÄÄZII = % & * 十一ぐÄ】ド十一亠 : I D ENT I F I ER ' (' declarator rp declarator2 ・ [ ' expr ' ] ・ dec I arator2 ' (' rp declarator2 ' ( ・ param-l ist rp dec larator2 ' ( ' error rp dec larator2 eqop asslgnop CO 0 Cd 0 0 , cd cd 0. Q. a. incdecop unop logor logand ・ type-spec dec larator param_dec ー - よる C コンバイラブログラミング 73 yacc に
[ 表 3 ] 処理系 MS-C Ve 「 5.1 MetaWare High C 386 MS-C Ve 「 5.1 (long) 令の直行性が増しているため , 機械語ルー 80386 は , 旧来の 8086 / 286 と比較すると命 プログラムの例 386asm を使った 広がる。 ンパイル / 実行してみると , さらに速度差は として整数演算をすべて 32 ビットにしてコ #define int long が 2 割ほど速い。ちなみに , MS-C のほうて、 算を行うわけだが , それて、も High C のほう C は 16 ビット幅 , High C は 32 ビット幅の演 演算速度を測るべンチマークなのて、 , MS- 果は表 3 のとおりて、ある odhrystone は int の を High C て、コンパイル / 実行してみた。結 dhrystone のべンチマークテストプログラム ます初めに , 本誌創刊号て、も使った dhrystone テスト ログラムは書けないようて、ある。 グラフィック VRAM に直接アクセスする ) プ ータセグメントにアクセスする ( たとえば , 秒数 8 (PC-9801RA 80386 16M 使用 ) 3846 6250 5000 dhrystone 値 チンをつくりやすい。そこて、 , ここて、もひ とっサンプルをつくってみた。付録ディス クの CMAGA YTOKUI YPTYE. ASM が それて、ある。このプログラムは指定された ファイルを IM バイトのバッフアに一度て、読 み込み , 読み込んだデータを一度て、標準出 力に書き出す , というものて、ある。リアル モードの MS ー DOS プログラムとそれほど雰 囲気が違わないのかわかると思う。 38 引 DOS-Extender まとめ 以上 Phar Lap 社の 386 ー DOS-Extender を紹介してきたが , 多量のワークエリアを 必要とするようなプログラムを書く人にと っては非常に有効なツールだと思う。とく に , MS-DOS のもつよさーーシステム内に あるものは I/O< もメモリて、も勝手放題にア クセスて、きる を失うことなしに , 4G バ イトリニアなメモリ空間て、プログラムてき るのは , 非常にうれしいものて、ある。この 製品は現在東洋工ンジニアリングから ' 98 版 がリリースされている。 DOS/16M (RarionaI Systems.lnc. ) さて , 今まて、 80386 の話しかしてこなかっ たが , 80286 にも DOS 工クステンダはある。 80286 は基本的には 16 ビット CPU なのて、 , リ アルモードて、もプロテクトモードて、も演算 のパワーは変わらないが , アクセスてきる メモリ空間は IM から 16M へと広がる。 DOS/16M もやっていることは 38 引 DOS -Extender とたいして変わらない。プロテク トモードて、プログラムを動かし , MS-DOS のファンクションコールが使われるときに は CPU をリアルモードに切り換えファンク 62 CMAGAZINE 1989 12 ションを実行する。 80286 の場合 , 80386 と 異なり , いったんプロテクトモードになる とプロセッサをリセットしないかぎりリア ルモードに復帰て、きない。しかし現存する ほとんどの 80286 搭載パソコンには CPU のリ セットヒ。ンに信号を送り込む I / O ポートが用 意されており , システムワークエリア内の 特定の場所に特定の値をセットした後て、 , その I/O ポートにデータを出力すると , シス テムをリプートすることなしに CPU にリセ ットをかけることがて、きるようになってい る。 MS-DOS のシステムディスクに付属し ている 80286 のプロテクトメモリを利用する RAMDISK や EMS ドライバなどがあるが , これらもこのメカニズムを使ってリアルモ ード / プロテクトモード間を行ったり来たり しながらデータを転送しているわけて、ある。 DOS/16M は , 38 引 DOS-Extender のよ うにアセンプラ , リンカのような開発キッ トをひととおり含んて、いるわけて、はない 付属しているのはデバッガのみて、ある。プ ログラムの作成には既存の MASM や C コン パイラ (LatticeC や MS-C など ) を流用し , リ ンク時にスタートアップルーチンとランタ イムライプラリを DOS / 16M のものに入れ替 えるという方法をとっている。 こうしてて、 きるプログラムは . EXE ファイルだが , それ を、、 MAKEPM" という DOS / 16M 付属のプ ログラムて、 . EXP ファイルに変換し , 、、 LOADER" て、そのプロテクトモードプロ グラムを実行するのて、ある。 付属しているデバッガ D はけっこう本格的 て、ある。画面はコード , データ , ダイアロ グなどのウインドウに分かれており , ソー スレベルのデバッグも可能になっている。 ソースレベルて、デバッグを行うときは , プ ログラムのコンパイル・リンク時に CodeView 用のオプションをつけて , CodeView 用のデ バッグ情報を埋め込んて、おくだけて、よい また , おもしろい機能としては , 一定間 隔て、実行中のコードのアドレスをサンプリ ングする sampling monitor がある。 これを使うと , プログラムのどこの部分 が多く実行されているかがわかり , 高速化 するとき , どこに重点をおいてチュー グすればよいのかが判断て、きる。 DOS/16M は , 日本て、はライフポートから LatticeC/EX, LatticeC/16M という名前て、 LatticeC コンパイラと抱き合わせて、発売さ れている。 DOS / 16M がサポートするのは、 LatticeC, MS-C, MASM, などがある。