授業/C言語基礎/演算子の高度な使い方 のバックアップソース(No.2)

これまでの勉強で、演算子は必要に応じて少しずつ出てきました。

ここでは、演算子について、まだ説明していない部分を説明します。


*オペランド [#m77529e0]

演算の対象となる値や変数を''オペランド''といいます。

オペランドが1つの演算子を''単項演算子''、2つのものを''2項演算子''、3つのものを''3項演算子''といいます。


*増分・減分演算子の前置と後置 [#a54f1eaf]

単項演算子である増分演算子 ''++'' は、オペランドの値を1増やします。
同じく単項演算子である減分演算子 ''--'' は、オペランドの値を1減らします。

増分・減分演算子は、演算子をオペランドの前に置くこと(前置)も、オペランドの後に置くこと(後置)もできますが、実は、振る舞いが異なります。

''前置''のとき、増分・減分演算子を含む式や命令文は、増分・減分演算子が適用されてから評価されます。

''後置''のとき、増分・減分演算子を含む式や命令文は、増分・減分演算子が適用される前に評価され、それから増分・減分演算子が適用されます。

次のプログラムは、増分演算子を前置と後置で使用しています(プログラム1)。
#geshi(c){{
  int i = 0;

  printf("%d\n", ++i);
  printf("%d\n", i++);
  printf("%d\n", i);
}}
最初のprintf関数は、前置の増分演算子なので、先に増分演算子が適用され、その後にprintf関数が実行されます。
次のprintf関数は、後置の増分演算子なので、先にprintf関数が実行され、その後に増分演算子が実行されます。

したがって、このプログラムを実行すると、次のようになります。
#geshi(sh){{
luna% a.out
1
1
2
}}



*ビット単位の演算子 [#jc89f6fc]

データは、すべて2進数のビット列で表されています。
そこで、整数を表す2進数のビット列に対してビット単位で演算する演算子が用意されています。
|優先順位|演算子|使用例|意味|h
|2|˜|˜a|補数|
|6|<<|a << b|左シフト|
|~|>>|a >> b|右シフト|
|9|&|a & b|ビット論理積|
|10|^|a ^ b|ビット排他的論理和|
|11|&#x7c;|a &#x7c; b|ビット論理和|

補数以外の演算子には、複合代入演算子 <<=, >>=, &=, ^=, |= もあります。


**補数 [#be12c9b2]
補数演算子 ''&tilde;'' は、ビットを反転させます。
#geshi(c){{
  int i = 0x00000010;

  int j = ~i
  printf("%08x\n", j);
}}
#geshi(sh){{
luna% a.out
ffffffef
}}

**左シフト、右シフト [#u4be1562]
左シフト演算子 ''<<'' はビット列を指定された数だけ左にシフトします。
1ビット左にシフトするごとに、値は2倍になります。

右シフト演算子 ''>>'' はビット列を指定された数だけ右にシフトします。
1ビット右にシフトするごとに、値は半分になります。

左オペランドが符号なし整数または符号付き整数で先頭ビットが 0(値が 0 または正)のとき、すべてのビットがシフトし、シフトによってはみ出たビットは無視され、空いたビットは 0 になります。

左オペランドが符号付き整数で先頭ビットが 1(値が負)のとき、どう処理するかは処理系に任されています。
#geshi(c){{
  unsigned int ui = 0x00000010;
  signed int   si = 0x80000001;

  printf("%08x, %08x\n", ui, si);
  printf("%d, %d\n", ui, si);

  ui = ui << 1;
  si = si << 1;

  printf("%08x, %08x\n", ui, si);
  printf("%d, %d\n", ui, si);
}}
#geshi(sh){{
luna% a.out
00000010, 80000001
16, -2147483647
00000020, 00000002
32, 2
}}




**論理積 [#i62e1f15]
ビット単位の論理積演算子 ''&'' は、ビットごとに論理積を取ります。
論理演算子の論理積演算子 ''&&'' と混同しないように注意しましょう。

1 と論理積を取るとそのビットは保存され、0 と論理積をとるとそのビットは0になります。
この性質を利用して、上位ビットや下位ビットだけを取り出す、マスク操作に用いられます。
#geshi(c){{
  int i = 0xaaaaaaaa;
  printf("%08x\n", i);

  int j = i & 0x0000ff;
  printf("%08x\n", j);
}}


**排他的論理和 [#ja94dc04]
ビット単位の排他的論理和演算子 ''^'' は、ビットごとに排他的論理和を取ります。

1 と排他的論理和をとるとそのビットは反転し、0 と排他的論理和をとるとそのビットは保存されます。
この性質を利用して、ビット列を部分的に反転させる操作に用いられます。
#geshi(c){{
  int i = 0xaaaaaaaa;
  printf("%08x\n", i);

  int j = i ^ 0x0000ff;
  printf("%08x\n", j);
}}
#geshi(sh){{
luna% a.out
aaaaaaaa
aaaaaa55
}}


**論理和 [#y37b82de]
ビット単位の論理積演算子 ''&'' は、ビットごとに論理積を取ります。
論理演算子の論理積演算子 ''&&'' と混同しないように注意しましょう。

#geshi(c){{
  int i = 0xaaaaaaaa;
  printf("%08x\n", i);

  int j = i | 0x0000ff;
  printf("%08x\n", j);
}}
#geshi(sh){{
luna% a.out
aaaaaaaa
aaaaaaff
}}



*論理演算子の短絡評価 [#ff7a051e]

論理演算子の論理積 ''&&'' は、いずれかが偽 (0) のときは結果が偽 (0) となり、そうでないときは結果が真 (1) となります。

そこで、論理積演算子は、左側のオペランドを先に評価し、結果が偽 (0) のときは、右側のオペランドを操作しないで結果として偽 (0) を返します。

同様に、論理演算子の論理和 ''&#x7c;&#x7c;'' は、いずれかが真(0 でない)のときは結果が真 (1) となり、そうでないときは結果が偽 (0) となります。

そこで、論理和演算子は、左側のオペランドを先に評価し、結果が真(0 でない)のときは、右側のオペランドを操作しないで結果として真 (1) を返します。

これを、論理演算子の''短絡評価''といいます。

#geshi(c){{
  int i = 0;

  (i > 0) && printf("正\n");
  (i > 0) || printf("0または負\n");
}}
#geshi(sh){{
luna% a.out
0または負
}}

この性質を利用して、除算を行う前に割る数が 0 でないことを確認したり、配列の要素にアクセスする前に要素番号が有効な範囲内にあることを確認するなど、実行時にエラーを起こさないようにするためのエラー・チェックに使うことがあります。


*配列添字演算子と関数呼び出し演算子 [#n51d4a9c]

配列の添字を表すときに使う角括弧 ''[ ]'' と、関数を呼び出すときに使う丸括弧 ''( )'' も、実は演算子です。
|優先順位|演算子|使用例|意味|h
|1|[ ]|a[b]|配列添字|
|~|( )|a(b)|関数呼び出し|

演算子であることを意識する必要はありません。


*演算子の優先順位 [#i79e91b7]

これまでに勉強した演算子の優先順位をまとめておきます。
|優先順位|演算子|使用例|意味|h
|1|[ ]|a[b]|配列添字|
|~|( )|a(b)|関数呼び出し|
|2|+|+a|正の数|
|~|&minus;|&minus;a|負の数|
|~|!|!a|論理否定|
|~|++|i++|値を1増やす|
|~|~|++i|~|
|~|&minus;&minus;|i&minus;&minus;|値を1減らす|
|~|~|&minus;&minus;i|~|
|~|&tilde;|&tilde;a|補数|
|3|()|(b) a|キャスト|
|4|*|a * b|積|
|~|/|a / b|商|
|~|%|a % b|剰余|
|5|+|a + b|和|
|~|&minus;|a &minus; b|差|
|6|<<|a << b|左シフト|
|~|>>|a >> b|右シフト|
|7|<|a < b|小さい|
|~|<=|a <= b|小さいか等しい|
|~|&gt;|a > b|大きい|
|~|>=|a >= b|大きいか等しい|
|8|==|a == b|等しい|
|~|!=|a != b|等しくない|
|9|&|a & b|ビット論理積|
|10|^|a ^ b|ビット排他的論理和|
|11|&#x7c;|a &#x7c; b|ビット論理和|
|12|&&|a && b|論理積|
|13|&#x7c;&#x7c;|a &#x7c;&#x7c; b|論理和|
|14|?と:|a ? b : c|条件|
|15|=|a = b|代入|
|~|+=|a += b|加算代入|
|~|&minus;=|a &minus;= b|減算代入|
|~|*=|a *= b|乗算代入|
|~|/=|a /= b|除算代入|
|~|<<=|a <<= b|左シフト代入|
|~|>>=|a >>= b|右シフト代入|
|~|&=|a &= b|ビット論理積代入|
|~|^=|a ^= b|ビット排他的論理和代入|
|~|&#x7c;=|a &#x7c;= b|ビット論理和代入|


*その他の演算子(おまけ) [#g6a22182]

上の表にない演算子は、C言語応用で勉強する構造体、ポインター、アドレスに関する演算子と、特に用途がない順次演算子だけです。

|優先順位|演算子|使用例|意味|h
|1|.|a.b|構造体の要素選択|
|2|->|a->b|構造体ポインターの要素選択|
|~|*|*a|ポインター参照|
|~|&|&a|アドレスへのアクセス|
|16|,|a, b|順次|



*2進数のビット列を作る [#gacf2a53]
#geshi(c){{
}}
トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS