2013年11月24日日曜日

C/C++ 演算

コンピュータで一番得意なのは足し算や掛け算などの演算でしょう。

value = 3 * 4 + (-4) / (1 + 1);

まずはデータとして値を代入する必要がありますが、それを示すのがイコール(=)です。C/C++では以下の点が注意です。
  1. 数学的なイコールと意味が違い、代入を意味する。
  2. イコールの左側を左辺値といい、変数の必要があり、変数の値を書き換える。
  3. イコール自身も式となっており、代入値を返す。
最後の3の意味が分かりにくいかもしれませんが、以下のように右辺に書くこともでき、足し算などと同様に扱うことができると言うことです。ある意味2がイコール特有の処理と言えるかもしれません。

value2 = (value = 3 * 4 - (-4) / (1 + 1));


残りの文はアスタリスク(*)が掛け算、スラッシュ(/)が割り算を示す演算を表すということ意外は数学的な記述とほとんど同じで意味が解ると思います。演算には優先順があり括弧内から計算して行きます。また加減算より乗除算の方を先に計算します。最初の記述では、4という結果がvalueに代入されます。演算を表す文字を演算子と言い、通常の計算に使う演算子は算術演算子と言い、他にも余り(剰余)の計算でパーセント(%)があります。

C/C++として特徴的な演算子として 代入と計算を一緒に出来る演算子があることです。これは別の書き方ができますが、よく使うことからソースコードを簡単に書くことができるようにしたものです。

同等の式備考
value++;value = value + 1;全体の評価が終わった後に+1する。
++value;value = value + 1;全体の評価を行う前に+1する。
value--;value = value - 1;全体の評価が終わった後に-1する。
--value;value = value - 1;全体の評価を行う前に-1する。
value += 10;value = value + 10;+=の他にも-=,*=,/=,%=,&=,|=,^=が使用できる。

++value; と++value;の違いは分かりにくいですが、a = ++value; a = value++;と他の演算と記述すると違いが分かります。前者では、aにvalue+1が代入され、後者ではaにvalueが代入されます。



C/C++らしい演算子としては、他に2進数での計算を行うビット演算子があります。00110を1のシフトの場合に01100と左にシフトする左シフト(<<)、00011と右にシフトする右シフト(>>)があります。またビットごと論理和、論理積、排他的論理和、論理否定をおこなう演算があります。論理和など聞きなれない言葉かもしれませんが、意外とよく使うため、覚えておいた方が良いです。

aba | b (論理和)a & b (論理積)a ^ b (排他的論理)~a (論理否定)
000001
011011
101010
111100



また用途は計算と全く違うのですが、C/C++では演算子となっているもので、比較演算子があります。下記は「もしaが3と等しかったらaを1引く」と言うプログラムですが、このa == 3 のような条件を指定する部分も比較を行う演算子となっています。

if (a == 3) a--;

比較演算子には小なり(<)、小なりイコール(<=)大なり(>)、大なりイコール(>=)、非等価(!=)、等価(==)があります。これらの演算の結果はC++の場合にはbool型と言う型となり、trueまたはfalseのどちらかの演算結果となります。C言語の場合には、int型で比較結果が真(正しい)のとき1を偽(間違っている)のとき0の演算結果となります。

 代入(=)も演算子であり、以下の文も記述としては間違いではないのでコンパイル時にエラーとなりません。等価(==)と間違えないように注意が必要です。(gccの場合、コンパイル時に-Wallというオプションを付けることで条件のところで代入(=)が使用されていた場合にで警告を出すことは可能です)

if (a = 3) a--;



また、このような真偽の専用の演算子として、論理演算子の論理否定(!)、論理和(||)、 論理積(&&)があります。ビット演算子と違いビットごとの演算はせず、値が0であれば0と、値が0以外であれば1として計算をします。

また論理和(||)、 論理積(&&)は唯一演算を端折る場合がある演算子なので注意が必要です。例えば以下の式の場合、a == 1の比較演算を行い、偽だった場合には、b == 2 と c = 2の処理は行いません。左から演算していくときにその後の演算がどうであっても、結果が偽となるためです。しかし、b == 2 については問題ないとしてもその後、c = 2 の代入が行われないことは、その後cの値を使用するときに前の比較演算の影響が出ることになります。

(a == 1) && ((b == 2) && (c = 2))


他にもprintf()という関数の呼出をこれまで使っていましたが、この記述も関数呼出の演算子とみなされます。そのため、 以下のような記述も可能です。

 a = a + printf("Hello");

上述のようにC/C++の記述は演算子の固まりで出来ています。これらの演算子は全て繋げて書くことができますが、全ての演算子には足し算と掛け算では掛け算を先に行うように優先順位と言うものが存在します。例えば以下の場合にはaにどのような値が入るでしょうか?

 a = 3 << 1 + 1;

答えは12になります。左シフト(<<)より加算(+)の方が優先順位が高いため 3 << (1 + 1)と同等になるためです。

さらに優先順位が同じ場合には、結合規則が存在し、左から右に計算していくものと、右から左に計算していくものがあります。例えば加算(+)は左から右で a + b + c + d の記述は ((a + b) + c) + d と同等です。代入(=)は右から左で a = b = c = d は a = (b = (c = d))と同等になります。

 詳しくはWikipediaなど他の資料を参考してください。 他にも様々な演算子があったり、演算子は意外と奥が深いですが、C言語は演算子の記述の固まりであることと、優先順位に困った時には括弧を付けて優先順位をコントロールすることなど、注意点を覚えておけば、まずは良いかと思います。

0 件のコメント:

コメントを投稿