3.2 代人式は、代人動作をする式であり、 左辺値代入演算子右辺値 3.2.2 式と演算子 の形式を取り、「右辺値の値を計算し、その結果を代人演算子の規則に従って、左辺値に適用する 式」のことをいいます。簡単に述べれば、 a = 10 ; や a = b; という式に示されているように、左辺値は データを格納する人れ物 ( 箱 ) 、右辺値はデータそのものか籀に格納されたデータのことをいいま すマ 43 。代入演算子には、表 3.13 に示すものがあります。 表 3.13 代入演算子一覧表マ 44 代入演算子 説明 代入する 加算する 減算する 乗算する 除算する 剰余する 左シフトし代人する 右シフトし代人する AND の結果を代入する OR の結果を代人する 意味 x = x lvalue x = x & value X = X > > value X = X くく value x= x%value x = x / value X = X ☆ value X = X ー value X = X 十 value X = value 排他的 OR の結果を代人する x 表 3.13 内の演算子を用いた文で、 たとえば、 x 十 = value : は、次の文と同じ効果を示すことを意味しています。 X マ 43 マ 44 x 十 value; く← , > > = , & = Ⅱ = , ^ = の作用については、応用編で詳しく説明しています。 「値」を代人演算子「 = 」によって左辺値 a の「籀の中」に格納します。 a = b ; という式では、「 a 」が左辺値、「 = 」が代人演算子、「 b 」が右辺値となります。この式の意味は右辺値である b の
構造体の代入では、無名の「穴」はどのようになるのでしようか。 普通は「穴」も含めて代人されるでしようが、 ANSI 規格では明確に決められてはいません。 すから、代入元の構造体と代人先の構造体を標準関数 memcmp マ 12 で比較するようなプログラミ グは避けるべきです。 9.1 構造体 (structure) タイトルを表示する printf 文に注意してください。リスト 9.1 では紙面の都合上、タイトルの文字 列を 2 行に分けて記述していますが、このように、文字列定数を 2 つに分けて記述することができ ます。 ANSI 規格では、文字列定数が連続して記述された場合は、それらを連結して扱うことに 、ン 実行結果 ( リスト 9.1 ) なっているからです。 Hinako これを実行してみると、次のように表示されます。 2 工 P 1234567 B 工 RTHDAY 19890225 ADDRESS Yokohama—shi Kanagawa Pref . TE も 045 ー 123 ー 4567 ー構造体の代入 ( コビー ) struct Tag1 { 構造体変数どうしの代人を行うには、通常の代入文で記述できますⅵ 0 a = b; int char がまったく同じでも、構造体タグが異なる変数どうしでは型が異なるため、代人はできませんⅵ 1 。 この例では、構造体変数 b の全メンバが構造体変数 a のメンバへ代入されます。しかし、メンバ struct Tag2 { int char / ☆これは誤りです ! Tag1 から Tag2 への代入はできません☆ / で V 10 マ 12 構造体の代入は ANSI 規格で定められたものです。 ANSI 以前の骨董的コンパイラでは構造体の代入をサポートしていな いものもありえます。 もし異なった型の構造体の代人をどうしても行いたいのなら、ポインタを用いて構造体を一度別の構造体に見せかけ、 型変換してから代入することになります。しかし、このような使い方をする場面はあまりないでしよう。 coffee break 9.2 は、このような例の数少ない例の 1 つです。 memcmp は、メモリの内容を比較する標準関数です。く string. h > に宣言されています。 301
2.2 数の計算をしてみよう が、 int はその中の 1 つで、整数を格納するための変数を用意するキーワードです。このキーワード の後に、用意したい変数の名前をコンマで区切って並べます。この文は、変数定義というもので、 コンビュータの中に名前付きの籀を用意させる作用があります。 0 変数を利用する 変数の用意ができたところで、その使い方について説明しましよう。変数への値の格納は、代入 ( assign ) という操作ーによって行います。代入はイコール ( = ) を用いて次のように記述します。 変数名 = 格納する値 , これは代入文 (assignment statement) と呼ばれるものです。プログラムの中で代入文が実行され ると、変数の中に値が格納されます。変数への代入は何度も行うことができますが、変数の籀には 同時に 1 つの値しか格納できません。代人を行うとその前に格納していた値は消えて、新しい値が 格納されます。この様子を次に示します。 マ 22 箱の中に 1 が格納される a 1 : —> a 1 箱の中に 2 が格納される a 2 ー —> a 2 1 ・算を行うためには、変数の中に格納された値を取り出す必要がありますが、これは簡単です。 変数名をそのまま記述すればよいのです。 変数名 計算式の中や代人の右辺に変数名があると、変数名に格納されている値があるのと同じことにな ります。 C では足し算を行う場合、プラス ( + ) を用いて、 1 + 2 のように書くことができます ( もちろ ん答えは 3 です ) 。これを変数 a と b を使って a + b のように書くことができるのです。変数 a と b に値 として 1 と 2 が代人されていれば、 a + b の答えは 3 になります。 一三ロ 箱の中身の値か計算に利用される a 十 b ー・ 1 + 2 このように、変数名を記述することによって、その変数に格納されている値を取り出し利用する ことを参照 (reference) と呼びます。 計算する内容によって、必要とするデータの精度はさまざまですが、一般に箱の大きさが大きい方がより高い精度の マ 20 データを保持できるといえます。 代入の他に、セット、設定、コビーなどということがあります。 マ 21 変数定義の段階で、籀の中にどのような値が入っているかは決まっていません。詳しくは 3.1.6 項で説明します。 マ 22
2 章 プログラミング入門 数定義、代人、参照などいろいろと新しい言葉が出てきました。これらは非常に重要な言葉ですの C で変数と呼ばれるものがあり、そこに値を格納できるということが理解できたでしようか。変 一計算するプログラム で、 ここで簡単に復習しておきましよう。 ・箱から値を取り出すこと ・箱に値をしまうこと 「変数定義」・・名前付きの箱を用意し、変数の準備をすること 「参照」 「代入」 それでは、実際のプログラムの中で変数を利用してみましよう。リスト 2.9 に、足し算をするプ ログラムの例を示します。これは 2.2.1 項の冒頭に示した足し算の手順を C のソースプログラムと int main ( VOid ) #include く stdio . h> リスト 2.9 足し算 して記述したものです。 土 n セ a ′ b ′ c ー a 十 b; C printf ( "c— return 0 : —%dYn" / ☆ 3 つの変数、 a ′ b ′ c を準備する / ☆ a に 1 を代入 / ☆ b に 2 を代入 / ☆ a と b を足して、その結果を C に代入 答えの表示 / ☆ このソースプログラムを詳しく解析していきましよう。このプログラムでは 3 つの変数 a,b,c を使 います。 inta , b , c ; が変数定義で、これらの変数を準備している部分です。このように、変数定義 は、プログラムの先頭にまとめて記述します。そして、変数 a と b に値 1 と 2 を代人した後、これら を足し算して変数 c に代入しています。足し算の式の中では、 a + b という形で、 a と b の値が参照 されています。プログラムが実行されるときに、変数 a,b,c の中身がどのようになっているのかを 次に示します。次のページの各変数の最初の状態は」となっていますが、これはその変数の値が 不明 , 23 なことを意味します。つまり、変数は値が代入されるまでは何が入っているかわからない ので、必す値を代入する必要があります。 変数の中には実際には何らかの値が入っているのですが、何が人っているか予測することはできません。 数の値が「未定義である」と呼びます。 42 マ 23 この状態を変
8.1 ポインタの基礎 char b double a double *PI char *P2 int *P3 ・図 8.4 ポインタには指し示す変数の型を指定する ー間接演算子 変数 x に「 10 」を代人するには、 X 10 : と書きましたが、ポインタ変数 p を使用して、変数 x に代人することができます。 P-4 X 1 土 A* ☆ / ☆変数 x のアドレスをポインタ p に代入 / ☆値 10 をポインタ p の指している場所 ( つまり、 x ) に代入 ☆ / このように、ポインタ変数 p にアスタリスク ( ☆ ) を付けることによって、そのポインタ変数が指 し示す内容にの場合、変数 x ) を間接的に参照することができるのです。つまり、 p の左辺値マ 14 ( 代 人の対象になる変数の場所 ) を返すことになります。「夫 p 」が代入の対象にならない場合、それはポ インタが指している変数の値 ( 右辺値マ 14 ) となります。つまり、上の例に続き、 a を int 型とすると、 *p; / ☆変数 a にホインタ p が指している変数 ( x ) の値 10 を代入☆ / によって、 a には 10 が代人されます。 この「夫」を間接演算子と呼びます。「司は、定義や宣言に現れるときは、ポインタとしての型マ 15 を作ることを示し、式の中でポインタ変数の前に現れるときは間接参照を示しています。この違い に注意してくださいⅵ 6 。 では、前項でポインタには、それが指し示す変数の型を指定しなければならなかった理由の 1 つ を明らかにしましよう。たとえば、 pl や p2 を型の指定がないポインタとして定義できたとして、 次のような演算を考えてみましよう。 3.2.2 項を参照してください。 このような型を派生型といいます。 マ 15 当然のことですが、 int 型や char 型、あるいは double 型の変数や演算結果の前に置かれた「☆」は乗算を意味します。 マ 16 275
3.2 式と演算子 また、このようなプログラム 0 型変換されて float 型データ 12.0X100 になって x に代入されます。 この例では、整数データ ( 12 ) を float 型の変数 x に代人しています。この場合、整数データ ( 12 ) が x = 12 ー f10a セ x; 次のプログラムを見てください。 ロ代入における型変換 に対し、コンパイル時にウォーニングを出すコンパイラもあります。 i の値を float 型で完全には表現できず、おかしな値になっています。 リスト 3.15 のプログラムは、 i の値が float 型の x と加算されたため、Ⅱ oat 型へと変換されますが、 土十 x = 123456792 . 000000 実行結果 ( リスト 3.15 戸 54 また、次の例では、代人演算子の実行順序がよくわかる例になっています。 整数データになります。 高い方から低い方への変換になります。この場合、 12.345 の小数点以下が切り捨てられ、 12 という この場合には、浮動小数点データ ( 12.345 ) が整数データに型変換されます。この場合は、精度の = 12 . 345 ー int k; きにも型変換が行われます。 それでは、整数型変数に浮動小数点データを代入したら、どうなるのでしようか。実は、このと リスト 3.16 型変換と代入演算子の順序 #include く stdio . h> 土 n セ main ( VOid ) int double x, y; printf ( "x=%f return 0 : 土 = 宅 d y=%fYn" X ′ 工′ Y レ マ 54 この結果は、 gcc でコンパイルを行った場合です。環境によっては実行結果のようにならす、 123456789.000000 と表示 されるものもあります。
文字列の表現 letter letter letter letter 1e セセ e て letter 65 ー 0X41 ー 0101 ー ー YI 01 ー ー Yx41 ー / ☆文字定数の代入 / ☆ ASCI 工文字 A を 10 進数表示した整数定数の代入 / ☆ ASCI 工文字 A を 16 進数表示した整数定数の代入 / ☆ ASCI 工文字 A を 8 進数表示した整数定数の代入 / ☆ ASCI 工文字 A を 8 進数表示した文字定数の代入 / ☆ ASCII 文字 A を 16 進数表示した文字定数の代入 7.1 ☆ / ☆ / ☆ / ☆ / ☆ / という具合に、 6 通り考えられます。なお、 10r は付録 3 に示すようにエスケープシーケンスを用 いた例で、 10 進数の 65 を 8 進数で表記しています。同様に、 X41 は 16 進数で表記したエスケープ シーケンスの例です。文字 'A' を代人しても 'A' は数値 ( 10 進数の 65 ) に置き換えられて評価されるの C の文字列は NULL 文字で終端するようになっていますが、 Pasc 引などの他の言語は別の方法で表 ス 1 C の文字列 -coffee-break で、 1 バイトの整数の代人と変わりないのです。このように、 C では文字型という型は存在しません。 現しています。 PascaI の文字列 C の文字列 先頭に文字列の長さの情報がくる 5 H' 0 0 ' YO' 文字列の最後を示す NULL 文字 249 方式と C 方式を両方備えており、効率化を図っています。 Microsoft 社のオブジェクト指向ライブラリの MFC には CString というクラスがあり、これは pascal 方式から PascaI 方式に変えたところ、約 20 % ほど実行効率が向上したという報告があります。また、 の使用を禁止する場合もありました。実際に、アセンブラシステムを開発したときに文字列表現を c 式の方が効率がよいのです。そのため、以前は、性能の悪いパソコン上での開発では C の文字列機能 しかし、近年のほとんどのパソコンが用いている Pentium 系の CPU では、 C 方式よりも pascal 方 文字列を効率よく扱うような機械語があったのです。 はこのような非効率な表現を使用しているのでしようか。実は、 C が開発されたマシンには C 形式の 長さを知りたい場合に、 C のようにすべての文字を調べる必要がなくなります。それでは、なぜ c で Pasc 方式の場合は、文字列の長さが文字列の先頭に情報として記録されていますので、文字列の
4.3 ループ処理 ( 繰り返し処理 ) ー getchar ( ) は、本来 01 100001 という値を返すべきところを int で返してきます。 この理由は、 EOF の値が一 1 と定義されていることにあります ' 56 。 singed char 型を用いると一 1 を 表現できますが、 char 型が signed か unsigned かは処理系によって異なります。したがって、 char 型 また、 i 猷型の一 1 は、 singedchar 型のー1 とは厳密には異なります。 で常に一 1 を表現できるとは限らないのです。 2 進数 1 1 1 1 1 1 1 1 signed char int(32bits) 1 6 進数 OxFF OxFFFFFFFF このように int で一 1 を表現すると 8bit しかない char 型では決して表現できない値にすることができ るのです。したがって、文字コードとしてはありえない値、 EOF を返すために、 getchar ( ) は int 型と して文字を返すようにしているのです。文字コードは、 0X00000000 から 0x000000FF までの値を用 また、 putchar(c) の場合には、引数 c の型は int 型になります。 い、 EOF は 0xFFFFFFFF を用います。 リスト 4.14 入力した文字を出力する ( その 2 ) getchar の EOF の判定と while 文の継続条件は、次のように記述することもできます。 getchar() の継続条件 ロ #include く stdio . h> int main ( V0id ) 土セ C : while ( (c = getchar ( ) ) putchar ( c ) ー return 0 ー これは、次のように考えます。 EOF ) ます、 getchar 関数が実行され、標準人力よりデータを取り込み ます。そして、その値が c に代人されます。つまり、 c = getchar ( ) がまず実行されます。代人演算の 結果は、代人された値そのもの , 57 ですから、 ( c = getchar ( ) ) という代入演算の結果の値は、標準入 カより取り込まれた値そのものになります。したがって、それが EOF かどうかを検査すればよい わけです。 マ 56 マ 57 この値は、インクルードファイル <stdio. h> の中に定義されています。 = 演算子は、十やーの演算子と同じように演算結果をもちました。たとえば、 p = ( t = 5 ) ; は、 ( t = 5 ) の演算結果 ( もちろん、 t には 5 が代入されています ) である 5 が p にも代入されました。このことについては、 3.2.1 項も参照してください。 141
変数と式 変数 = 値 : といった単純な構成をした文として学習してきましたが、 c ではさらに、『「変数 = 値」は代入式で ある』といった概念に拡張しています。また、演算された式は必ず値をもっことが c の特徴ですマ 40 。 たとえば、代人式 a = b は演算の結果、式の値は b の値が代人された a の値となります。このことを 使い、以下のような式を構成することができます。 a = 3 ☆ ( b = 2 レ 上の式では、まず式 b = 2 が評価されます。数学の式と同じように、 C の式でも ( ) の中が先に処 理されます。その結果、 b に 2 が代入され、その式の値は 2 になります。つまり、上の式はこの時 点で次のようになります。 では、次の式はどうでしようか。 次に、 3 ☆ 2 が処理され、 6 が a に代入されます。 3 ☆ 2 : a + c) を先に計算し、別のコンパイラでは (d + e) を先に計算します。それでは、次の式を見てくださ ちらを先に計算するかはコンパイラの開発者が自由に決めてよいのです。ある種のコンパイラは (b 規定されていません。この場合は、どちらが先に処理されても a の値に変わりはありませんが、ど この式には ( ) が 2 つありますが、 (b + c) と (d + e) のどちらが先に処理されるかは、 c では明確に a ( i = 3 ) ☆ ( i + 3 レ 上の式では最初の括弧が先に処理された場合は a の値は 18 、 2 番目の括弧が先に処理された場合 は 9 となります。つまり、 ( ) の実行順序の違いによって、計算される a の値が異なってしまうので すマ 41 。このようなプログラムは、コンパイラによって実行結果が異なってしまいます。そのため、 このようなプログラムは決して書かないようにしましよう。また、式の中で使用する演算子には優 先順位と結合規則が決まっていますマ 42 。それらにも注意をする必要があるでしよう。 V 40 マ 41 マ 42 void 型という特殊な型により、値をもたない式 ( 関数 ) があります。この型については 8 章で説明します。 実は、この式は副作用完了点に関する文法上の違反をおかしていますが、このことをチェックするコンパイラは少ない ようです。副作用完了点については、スーパーリファレンス編を参照してください。 3.2.6 項で説明します。 86
3.2 式と演算子 は構文として正しくありません。その理由は、左辺値と右辺値の関係を考えると容易に理解するこ とができます。すなわち (a = b) の代入式の結果は、 b の値となり、変数 a や b そのものを示すわけ ではないからです。もし、 b の値が 5 の場合、 (a = b) + = 3 ; は と評価され、文法違反となります。 5 十 = 3 : となり最終的に 5 ) 十 = 3 : 3.2.3 整数型データ 浮動小数点型に対するものがあります。 コンヒ。ュータの演算回路 ( 加算回路・乗算回路 ) は、次の図 3.2 に示すように整数型に対するもの、 整数型データ 浮動小数点データ 浮動小数点データ 整数型の演算回路 浮動小数点型の演算回路 ・図 3.2 コンヒ。ュータの演算回路 整数型データ 浮動小数点データ このため、多くのコンヒ。ュータ言語では、演算に関する規則が整数型に関するものと浮動小数点 型に関するものとに分かれています。 本節では、まず最初に整数型のデータに対する演算方法について学習します。演算を行うために は、変数の中にセット ( 代人 ) された値を用いて計算を行う算術式を用います。 一般には、算術式は通常の数式とほば同じですが、「☆」や「 / 」などの多少見慣れない演算子も ありますので、その違いを十分に理解してください。 ロ 整数型の演算子 C の算術式では、加算と減算はそれぞれ十とーを用いますが、乗算は x でなく☆を用います。ま た、キーポードに + のキーがありませんので、除算はスラッシュ ( / ) で代用します。すなわち、 a x 89