情報処理Ⅱ 第4回 2007年10月29日(月)
本日学ぶこと 演算子(Operator) 問題 +, -, =, %, ++ ,&& ,||, == など C言語ではどんな演算子が使用できるか? 演算子間の優先順位はどうなっているか? 例: 1 + 2 * 3 と書けば,(1 + 2) * 3 ではなく 1 + (2 * 3) と解釈される.このとき,* は + よりも 優先順位が高いという. 問題 整数が与えられたとき,そのビットパターンを出力できる? 例: (char型の) 40 のビットパターンは,00101000 int型やlong型,float型のビットパターンは?
演算子一覧 関数呼び出し( ),配列の添字[ ],構造体のメンバ参照(., ->) 単項演算子(!, 単項*, ++, 単項+, sizeof, キャストなど) 乗除(2項*, /, %) 加減算(2項+, 2項-) シフト(<<, >>) 関係(<, >, <=, >=) 関係(==, !=) ビット演算(&) ビット演算(^) ビット演算(|) 論理演算(&&) 論理演算(||) 3項演算子(? :) 代入演算子(=, +=など) コンマ(,) 高い 優先順位 低い 優先順位を変えたければ,( ) で囲む.( ) が何重にもなることがある.{ } や [ ] は,この目的で使用できない. 入p.314 リpp.155-156
単項演算子 増分演算子,減分演算子: ++, -- 正負の符号: +, - sizeof演算子: sizeof キャスト演算子: (型名) sizeof(char) は 1 char x; に対して sizeof(x) も 1 キャスト演算子: (型名) 1/3 は 0 (float)1/3 は 0.333… (float)(1/3) は 0.0 実数型やポインタ型の変数もオペランドにできる. オペランドは変数名,式 もしくは型名. 慣習として ( ) をつけるが, 関数呼び出しではない. ( ) は必須 リp.158
単項演算子:注意点 原則として,オペランドの前(左)につける. 増分演算子と減分演算子はオペランドの後ろにつけることもできるが,前につけるのと意味が異なる. x = --y; ⇒ 「y = y - 1; x = y;」 と同じ(減分を先に評価). x = y--; ⇒ 「x = y; y = y - 1;」 と同じ(減分を後で評価).(x = y)--; ではない. 増分演算子と減分演算子のオペランドは,左辺値であり,かつ算術型またはポインタ型でなければならない. 入pp.209-210 リp.158
ビット演算子 整数値をビット(0と1)の並びとみなし,ビット単位(bitwise)で演算を行う. ビット単位AND: & (2項演算子) ビット単位OR: | (2項演算子) ビット単位XOR: ^ (2項演算子) ビット単位NOT: ~ (単項演算子) 例: 1 & 2 は0である. 優先順位は ~ > & > ^ > | 代入演算子 &=, |=, ^= も利用可能. 入p.203-204 リp.161
ビット演算 ビット単位の演算結果であることに注意. x y 1 x & y x | y x ^ y x ~x 1 1 ビット単位の演算結果であることに注意. 0 ^ 1 = 1 ^ 0 = 1 (相補律) 1 ^ 1 = 0 (ビットの反転), x ^ x = 0 (ビットのクリア)が成り立つ.
ビット演算子の利用例 一部のビットを0にする. 一部のビットを1にする. 一部のビットを反転する. 全部のビットを反転する. char x = 35; のとき x & 0x0f は 3,x & 0xf0 は32. 一部のビットを1にする. char x = 0x23; のとき x | 0x0f は 0x2f,x | 0xf0 は0xf3. 一部のビットを反転する. char x = 0x23; のとき x ^ 0x0f は 0x2c,x ^ 0xf0 は0xd3. 全部のビットを反転する. x = ~x; とすればよい. x=35 : 00100011 0x0f : 00001111 x & 0x0f : 00000011 x | 0x0f : 00101111 x ^ 0x0f : 00101100 ~x : 11011100 入p.205
シフト演算子 整数値をビットの並びとみなす. 左シフト: << 右シフト: >> いずれも2項演算子で,左オペランドは整数値,右オペランドはシフトするビット数(0以上の整数値). 5 << 3 は 40 m << n は m×2n m >> n は m×2-n 負の数を右シフトしたときの結果は処理系依存. 代入演算子 <<=, >>= も利用可能. 5 → 00000101 ← 40 >> 3 5 << 3 → 00101000 ← 40 入pp.205-206, p.123 リp.159
ビットパターン出力プログラム:準備 対象(入力)は,unsigned char型の値 x=40 … 1 1 簡単のため ビット長が分かれば,他の整数型にも適用可能 x=40 … 1 1 char40.c
ビットパターン出力プログラム … 検査方法 x: 1 1 b: 1 b: 1 b: 1 b: 1 bの初期値は,1 << 7 1 1 b: 1 x & bは0 ⇒ 0 を出力 b: 1 x & bは0 ⇒ 0 を出力 b: 1 x & bは非0 ⇒ 1 を出力 … b: 1 x & bは0 ⇒ 0 を出力
3項演算子 オペランド1 ? オペランド2 : オペランド3 まずオペランド1を評価する.それが真であればオペランド2を,偽であればオペランド3を評価して,その値を演算結果とする. 例: t = (a > b) ? a : b; ⇒ if (a > b) {t = a;} else {t = b;} と同じ 演算結果は左辺値ではない × a > b ? a : b = 10; ○ *(a > b ? a : b) = 10; 3項演算子の入れ子も可能だが,読みにくい a, bはポインタ変数 入p.216 リpp.162-163
これらは構文の一部であり,演算子ではない. その他の演算子 関数呼び出し: ( ) 配列の添字: [ ] コンマ演算子: , for (i=0, max=-1; scanf("%d", &d[i]) == 1; i++) これらは構文の一部であり,演算子ではない. リp.157, p.165
補足 Cらしい式の例 結合規則 代入の順序
Cらしい式の例 xの値を1だけ増やす xの値を倍にする xが偶数か奇数か判定する xが0か否か判定する x++; 実数なら,x *= 2; if (x & 1) 初回授業で紹介した「if (x % 2)」は,やや非効率 xが0か否か判定する if (!x) ただし,if (x == 0) を推奨する
結合規則 同じ優先順位の演算子が並ぶ場合にも,評価の順序があり,「結合規則」または「結合の向き」と呼ばれる. 左から右への結合(左結合) x = y - z + 2; ⇒ x = (y - z) + 2; と同じ 右結合でない演算子 右から左への結合(右結合) x += y = z + 2; ⇒ x += (y = z + 2); と同じ 単項演算子,3項演算子,代入演算子 入p.314 リp.148
代入の順序 オペランドの評価順序は,特に明記したものを除いて,不定(処理系依存)である.式の中で同一の変数などに2つ以上の代入をしないよう心がける. 例: x=2; printf("%d %d", ++x, ++x); の出力は 「3 4」かもしれないし,「4 3」かもしれない. 例: x % 2 ? (x = x * 3 + 1) : (x /= 2); は意図通りに動作する(が,if文で書くほうが自然). 「副作用完了点」という概念もあるが,授業では説明しない. リp.177
まとめ Cでは多彩な演算子が利用できる.いくつかは数学の記号に近く,いくつかはC独特である. 演算子ごとに「優先順位」と「結合規則」が決まっている. 真偽の扱い,評価されない式にも注意する.
値の評価の注意点 オーバーフロー アンダーフロー 計算誤差 暗黙の型変換
範囲を越えるとどうなるか? signed char型変数に13*13を格納すると? 13はint型定数で,13*13は,int型の169と評価される. これをsigned char型変数に格納しようとすると,169から256を引いた-87が代入される. signed char a = 127; a++; だと,aの値は128ではなく-128になる. 値が,型の取り得る範囲を超えるために,変わってしまうことを,オーバーフロー(桁あふれ)という. 入p.134 リp.151 13x13.c
無限小? 1から始めて2でどんどん割っていくと,いずれは0になる. 浮動小数点数の計算で,表現できる精度よりも小さくなり,値が変わる(0などになる)ことを,アンダーフローという. double d; for (d = 1; d > 0; d /= 2) { printf("%g\n", d); }
1なのに,1でない? for文を用いて,0, 0.1, 0.2, ..., 1.0 を取り出す,間違った方法 0.1という値を,計算機内で誤差なく表現できない.「計算誤差」により,0.1×10が1でないという事態が起こる. プログラミングの心がけ (最善) 実数型はループ用変数にしない. (次善) != ではなく,> や < を用いて判定をする. double d; for (d = 0; d != 1; d += 0.1) 入pp.136-137 リp.66 from0to1bad.c
異なる型の値同士の計算 1/3 と 1.0/3 と 1/3.0 と 1.0/3.0 は同じ値? 算術演算において,大きい範囲の型に揃えられる. int > short, int > char (汎整数拡張) long double > double > float > long long > long > int (暗黙の型変換) unsigned > signed プログラミングの心がけ unsignedとsignedの整数値を混在させて演算しない. 1/3 は,0.それ以外の上の式はすべて0.333… リpp.150-153
代入時の型変換 代入演算子では,必ず左辺の型に変換される. double d; int x; に対して x = d = 1.0/3.0; ⇒ d = 0.333…; x = 0; と同じ d = x = 1.0/3.0; ⇒ x = 0; d = 0.0; と同じ 代入演算子を使って複数の変数に代入するときは, 変数の型,および代入する値を同じものとする(例えば「x = y = 0;」)のが通常である. 一つの文で複数の変数に値を格納することを,一括代入という. プログラミング言語によっては,「x, y = 1, 2;」といった書き方で一括代入ができるが, Cでこの式は,二つあるカンマが演算子であり,優先順位に注意すると結局, 「y = 1;」となってしまう.
まとめ オーバーフロー,計算誤差,型変換に注意.