情報処理Ⅱ 2005年10月28日(金)
本日学ぶこと 演算子(Operator) 問題 +, -, =, %, ++ ,&& ,||, == など C言語ではどんな演算子が使用できるか? 演算子間の優先順位はどうなっているか? 例: 1 + 2 * 3 と書けば,(1 + 2) * 3 ではなく 1 + (2 * 3) と解釈される.このとき,* は + よりも 優先順位が高いという. 問題 整数が与えられたとき,そのビットパターンを出力できる? 例: (char)40 のビットパターンは,00101000 int型やlong型,float型のビットパターンは?
用語説明 演算対象をオペランド(operand)という. 代入できるオブジェクトを左辺値(lvalue)という. y = x + 1; のとき, x と 1 は,演算子 + のオペランド y と x + 1 は,演算子 = のオペランド 代入できるオブジェクトを左辺値(lvalue)という. 例: p = (++q) + 1; としたときの p と q 演算子を適用して値を得ることを評価という. 例: if (x = 3) {...} としたとき,xに3が代入されるとともに,ifの条件は真となる. 「left value」の略
演算子のすべて 関数呼び出し( ),配列の添字[ ],構造体のメンバ参照(., ->) 単項演算子(!, 単項*, ++, 単項+, sizeof, キャストなど) 乗除(2項*, /, %) 加減算(2項+, 2項-) シフト(<<, >>) 関係(<, >, <=, >=) 関係(==, !=) ビット演算(&) ビット演算(^) ビット演算(|) 論理演算(&&) 論理演算(||) 3項演算子(? :) 代入演算子(=, +=など) コンマ(,) 高い 優先順位 低い 優先順位を変えたければ,( )で囲む.( )が何重にもなることがある.{ }や[ ]は,この目的で使用できない.
2項演算子 加減算演算子: +, - 乗除演算子(1): *, / 乗除演算子(2): % (剰余を求める.オペランドは整数) アスタリスク 加減算演算子: +, - 乗除演算子(1): *, / 乗除演算子(2): % (剰余を求める.オペランドは整数) 関係演算子: <, >, <=, >=, ==, != 暗黙の型変換が起こる場合を除き,オペランドは同一の型でなければならない. ○ if (2 == 3.14) … × if ('a' == "abc") … ○ if (0x61 == 'a') … ○ if ("abc" == "def") … スラッシュ
単項演算子 増分演算子,減分演算子: ++, -- 正負の符号: +, - sizeof演算子: sizeof キャスト演算子: (型名) sizeof(char) は 1 char x; に対して sizeof(x) も 1 キャスト演算子: (型名) 1/3 は 0 (float)1/3 は 0.333… (float)(1/3) は 0.0 実数型やポインタ型の変数もオペランドにできる. オペランドは変数名,式 もしくは型名. 慣習として ( ) をつけるが, 関数呼び出しではない. ( ) は必須
単項演算子:注意点 原則として,オペランドの前(左)につける. 増分演算子と減分演算子はオペランドの後ろにつけることもできるが,前につけるのと意味が異なる. x = --y; ⇒ 「y = y - 1; x = y;」 と同じ(減分を先に評価). x = y--; ⇒ 「x = y; y = y - 1;」 と同じ(減分を後で評価).(x = y)--; ではない. 増分演算子と減分演算子のオペランドは,左辺値であり,かつ算術型またはポインタ型でなければならない.
代入演算子 =, +=, -=, *=, /=, %= など 左側のオペランドは左辺値でなければならない. 右から左に結合する. x 記号= y; は,x = x 記号 y; と同じ. 左側のオペランドは左辺値でなければならない. x + 1 = y; はNG. 右から左に結合する. x += y = z + 2; ⇒ y = z + 2; x += y; と同じ 左辺値に格納される値を演算結果とする.暗黙の型変換に注意. double d; int x; に対して x = d = 1./3.; ⇒ d = 0.333…; x = 0; と同じ d = x = 1./3.; ⇒ x = 0; d = 0.0; と同じ
ビットパターン出力プログラム:準備 対象(入力)は,unsigned char型の値 x=40 … 1 1 簡単のため ビット長が分かれば,他の整数型にも容易に拡張可能 x=40 … 1 1
ビットパターン出力プログラム(1) … 検査方法 x: 1 1 x: 1 1 x: 1 1 x: 1 x: 1 1 x>=128は偽 ⇒ 0を出力 x: 1 1 x>=128は偽 ⇒ 0を出力 x: 1 1 x>=128は真 ⇒ 1を出力 x: 1 x>=128は偽 ⇒ 0を出力 … x: x>=128は偽 ⇒ 0を出力
論理演算子 AND: && (2項演算子) OR: || (2項演算子) NOT: ! (単項演算子) 優先順位は ! > && > || 真偽とCでの表現 真: 0以外に評価される値 偽: 0に評価される値 例: 1 && 2は真(具体的には,1)である. && の左を偽と評価すれば,右は評価しない. || の左を真と評価すれば,右は評価しない. int a[10]; … if (x >= 0 && x < 10 && a[x] == y) とすると,x<0 や x≧10のときは a[x] == y を評価しない.
論理演算 偽:整数型なら0,浮動小数点型なら0.0,ポインタ型ならNULLと等しくなる値. x y x && y x || y x !x 真 真 1 1 真 偽 真 1 偽 1 真 偽 1 偽 偽 偽:整数型なら0,浮動小数点型なら0.0,ポインタ型ならNULLと等しくなる値. 真:偽でないスカラ型(整数型,浮動小数点型もしくはポインタ型)の値.
ビット演算子 整数値をビット(0と1)の並びとみなし,ビット単位(bitwise)で演算を行う. ビット単位AND: & (2項演算子) ビット単位OR: | (2項演算子) ビット単位XOR: ^ (2項演算子) ビット単位NOT: ~ (単項演算子) 例: 1 & 2 は0である. 優先順位は ~ > & > ^ > | 代入演算子 &=, |=, ^= も利用可能.
ビット演算 ビット単位の演算結果であることに注意. 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
シフト演算子 整数値をビットの並びとみなす. いずれも2項演算子で,左は整数値,右はシフトするビット数(0以上の整数値). 左シフト: << 右シフト: >> いずれも2項演算子で,左は整数値,右はシフトするビット数(0以上の整数値). 5 << 3 は 40 m << n は m×2n m >> n は m×2-n 負の数を右シフトしたときの結果は処理系依存. 代入演算子 <<=, >>= も利用可能. 5 → 00000101 ← 40 >> 3 5 << 3 → 00101000 ← 40
ビットパターン出力プログラム(2) … 検査方法 x: 1 1 b: 1 b: 1 b: 1 b: 1 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はポインタ変数
これらは構文の一部であり,演算子ではない. その他の演算子 関数呼び出し: ( ) 配列の添字: [ ] コンマ演算子: , 例: for (i = 0, p = function(p[i], 1); i < 10; i++) これらは構文の一部であり,演算子ではない.
代入の順序について オペランドの評価順序は,特に明記したものを除いて,不定(処理系依存)である.式の中で同一のオブジェクトに2つ以上の代入をしないよう心がける. 例: x=2; printf("%d %d", ++x, ++x); の出力は 「3 4」かもしれないし,「4 3」かもしれない. 例: x % 2 ? (x = x * 3 + 1) : (x /= 2); は意図通りに動作する(が,if文で書くほうが自然).
まとめ Cでは多彩な演算子が利用できる.いくつかは数学の記号に近く,いくつかはC独特である. 演算子ごとに「優先順位」と「結合の方向」が決まっている. 真偽の扱い,評価されない式にも注意する.