C言語教室 第9回 - 演算子いろいろ
C言語教室の中でもC言語の演算子は、どの言語でも使われるような算術演算子を始め、見ればわかるだろうというものは、特に説明無しで使ってきました。時々、こんな演算子があるんだよという例として、インクリメント演算子(++)や特殊な代入演算子(+=)などを出していました。
そろそろ演算子についての整理をしておきます。演算子には大きく分けて
算術演算子
比較演算子
代入演算子
インクリメント・デクリメント演算子
条件演算子
シフト演算子
があります。名前からその役目も想像できるとは思いますが、個別に見ていきましょう。
算術演算子
+ 加算
- 減算
* 乗算
/ 除算
% 除算の余り
いずれも二項演算子で、演算子の左と右の値で演算を行って結果を返します。乗算演算子(*)は、ポインタ演算子(こちらは単項演算子)および型指定と紛らわしいので注意が必要です。また除算演算子は整数同士である場合は整数除算が行われ結果が整数になります。左右が整数の場合はたとえ小数型に代入しても整数に切り捨てられるので、小数で結果が得たい場合には、左右どちらかの値の型が小数になっていなければなりません。
ちなみに掛け算2つ(**)で累乗演算子というのはC言語にはありません。累乗は関数を呼びます。
比較演算子
> 大なり
=> 大なりイコール
< 小なり
<= 小なりイコール
== イコール
!= ノットイコール
等値演算子(==)はイコール2つです。他の言語ではイコールひとつのこともありますが、C言語でイコールがひとつだと代入演算子として扱われ代入した値が結果となります。エラーになるわけではないので、うっかりしてもコンパイラは教えてくれません。また等値を含む比較演算子(>=,<=,!=)はイコールが後ろにあるもののみです。イコールを先に書くと(=>,=<,=!)エラーとなります(幸い他の目的で使われていません)。BASICのような不等演算子(<>)はありません。
代入演算子
= 代入
+= 加算して代入
-= 減算して代入
*= 乗算して代入
/= 除算して代入
%= 剰余して代入
C言語の代入はイコール一つです。Pascal系言語のような代入演算子(:=)はありません。比較演算子はイコール2つ(==)になります。
イコールの前に算術演算子を書く記法は、いずれも代入する左辺が右辺に出てくるのを省く書き方で、a = a + 5; と書くところを a += 5; のように使います。単純な使われ方をしていれば間違えないとは思いますが、i += j + 2; などとサラッと書いてある時に、i = i + j + 2; を意味していることを見落とさないようにしましょう。
インクリメント、デクリメント演算子
++ インクリメント
-– デクリメント
インクリメントは+1すること、デクリメントは-1することですが、少しややこしいところがあります。
また整数の場合は+1/-1ですが、ポインタの場合は参照する型に合わせた値が増減します(例えばint*であればアドレスは+4/-4になります)。なお小数の場合(float,double)は、1.0だけ+-されるようですが、ちょっと心配なので使わないようにしています。
論理演算子
|| 論理和 どちらかの条件が成立すれば真
&& 論理積 条件が両方成立すれば真
! 否定 真のときは偽。偽のとき真。
先に論理演算子を説明します。条件式に使われる演算子で、条件のAND、OR、NOTをこれらの演算子で表現します。論理演算の場合は記号を2つ続けます。ひとつだけだと次に説明するビット演算子として解釈されるのですが、たまたま否定は記号が被らないのでひとつだけです。
C言語の場合、条件式に代入などの処理も書けてしまうのですが、ANDでもORでも評価途中で結果が出る場合、すべての条件式を評価せずに処理を打ち切ることがあるので、ここに代入などがあっても実行されないことがあります。あとから条件を付け加えた時にやらかすことが多いので、代入を行う式が書いてある場合、確実に式が評価されるのかを確認する必要があります。
ビット演算子
& AND。両方1のときに1、それ以外は0
| OR。両方0のときのみ0。それ以外は1
^ XOR。両方が異なる値の時に1。それ以外は0
~ NOT。0は1、1は0に反転
>> シフト演算で右へずらす(すべてのビットの桁を移動する。符号を考慮するかしないかで演算の方法が異なる)
<< シフト演算で左へずらす
これらの演算子は値を2進数のパターンとしてビット毎の演算を行い結果を得ます。
シフト演算子は算術演算子で表現すれば a << 1; は a * 2; ですし、a >> 1; は、a / 2; と同じ結果になりますが、シフト演算子を使ったほうが、より効率的なバイナリが出力されることも期待できますし、目的によってはシフト演算子の方が意味を適切に表現することにもなります。
左右が同じ型であればわかりやすいのですが、異なる型の場合、どのような結果となるかは良く確認する必要があります。特に符号の有無が異なったりサイズが異なる場合にどうなるかは、簡単なコードで値を確認してみてください。型変換のタイミングが思った通りであるか間違うことが多いところです。
なおビット演算子に対しても代入演算子が使えます。
&=
|=
^=
~=
>>=
<<=
最後の2つの演算子は記号が3つも必要ですね。ひとつ足りないとまったく別の意味になるので、ご注意を。
三項間演算子と逐次演算子
? :
これは珍しく3つの値からひとつの結果を得る書き方で、最初に条件式を書き、それが真の時は?の次の値、偽の時は:の次の値が、全体の値となります。if文と似たような効果がありますが、式の一部として使えるので、コンパクトな表現が可能です。,
ひとつの式しか書けないときでも、カンマ演算子を使って複数の式を押し込むことが出来ます。式としての値は最後に評価した右側の値となります。for文で使われることが多い演算子です。
その他
単項演算子として符号反転(-)があります。ポインタ演算子(*)はポインタ変数の指す先の中身である値、アドレス演算子(&)は、変数を指すアドレスを返します。
他にも構造体で用いるドット演算子(.)とアロー演算子(->)は構造体とセットで覚えてください。また関数呼び出しであるとか配列記号([])も演算子の仲間ですが、特に説明はいらないと思います。
またsizeofという演算子があり、これは関数ではなく演算子として扱われていますが、演算子の後に書いた型に必要なメモリ領域の大きさを返すものです。
これら演算子は同じ記号が単項か二項かで意味が変わるものもありますし、複数の記号の組み合わせでひとつの演算子として解釈されるものもあるので、うっかりミスを招きやすいのですが、英語の単語を書くこともなく短い表現ができるのがCの言語の特徴でもあるので、慣れてください。また*/が+-よりも優先順位が高く、1 + 2 * 3 が 7 になるのは算数で習った通りですが、これだけ多くの演算子があると組み合わせによっては思った通りの順序で計算されないこともシバシバなので、自信がなければ必ず括弧を使って明示する癖をつけてください。
そういえば符号そのままという単項演算子(+)もあるのですが、特に効果はないので使うことはないと思います。
【C言語入門】演算子の使い方一覧(優先順位/余り/インクリメント)
【C言語講座】演算子一覧表
C の演算子一覧
https://www602.math.ryukoku.ac.jp/Prog1/cops.pdf
今回は課題をお休みします。次回は文字種判定についてです。
ヘッダ画像は 以下のサイトで作成しました。
https://huggingface.co/spaces/stabilityai/stable-diffusion