今回は、基本以外の演算子についてまとめて解説していますので、少し内容が長いです。一気に読み切るのでは無く、必要に応じて VS2022 で実際の動作を確認しながら理解を進めてください。
--
いきなりここに飛んで来ちゃった人は、よろしければ下記からご覧ください。
C言語基礎講座インデックス
- ±1 専用命令
これで n1 は +1 されて 11 が格納されています。n1 = n1 + 1; と同じですが、見ての通り書き方としてはすごくシンプルになります。これをインクリメントと呼びます。-1 は(想像が付くかもですが)以下のように記述します。
int n1 = 10; n1++;
これで n2 は 4 になります。-1 の場合はデクリメントと呼びます。かなり独特な書き方ですが、C言語でプログラムを記述していくと、この書き方は大変多くの場面で見かけます。さらに、
int n2 = 5; n2--;
こんな書き方も出来ます。前に配置するので前置(ぜんち)インクリメントと呼びます。先の n1++ は後置(こうち)インクリメントと呼びます。個人には前置の書き方が好きです。理由は昔のコンパイラだと、n1++; と書くより、++n1; と書く方が明らかに高速なバイナリを出力したためです。今は最適化が進んだため、実行速度の差は感じなくなりましたので、どちらでも良いと思います。
++n1;
※ 2002年当時調べ
…が、実はこれ、記述場所によってはどちらでもいいわけではない落とし穴があります。それが計算順序です。計算した結果を続けて代入できるという事は、こんな書き方も出来るのです。
この結果の ans の値を予想してください。PR 挟みますw
int n1 = 11; int n2 = 3; int ans = ++n1 / n2;
Shen Zhen Shi Ou Fu Si Technology Co., Ltd
どうですか、予想できましたか。この場合は、n1 が ++ でインクリメントされて 12 になり、その結果が n2 の値である 3 で割られて ans に代入されて 4 になります。続けて確認です。
この結果は、鋭い人なら気がついたかもですが、11 がインクリメントされる前に n2 で割られてしまうので、11÷3 の計算結果である 3.666666 が暗黙の型変換が入って 3 になるのです。そして、その後で、n1 が +1 されて 12 になるという動作をします。
int n1 = 11; int n2 = 3; int ans = n1++ / n2;
このインクリメントとデクリメントの複合計算は、C言語では大変多く用いられます。まずは最もシンプルなこの状態で、動作の理解が出来るようにしておいてください。VS2022 では、下記のプログラムを入れてステップトレースすることで、変数の変化が見て取れると思います。
#include <stdio.h> int main() { int n1, n2, ans; n1 = 11; n2 = 3; ans = ++n1 / n2; printf("前置インクリメントの結果は%dです。\n", ans); n1 = 11; n2 = 3; ans = n1++ / n2; printf("後置インクリメントの結果は%dです。\n", ans); }

printf で補足です。文字列の中に "¥n" と書くと改行します。もし、¥n を付けないと、ずらずらと後ろに繋がって表示されます。これはエスケープシーケンスと呼ばれています。書式文字列だとか、エスケープシーケンスだとか、C言語では文字列に意味を持たせていて、それも理解を阻む原因になっています。全部覚える必要は無いので、使う分だけ覚えていけば良いと思います。あるいは使うたびに調べるでも良いかと思います。
- シフト演算子
シフト演算を語る上でどうしても理解しておかねばならないのが2進数です。2進数の 0 と 1 の事を bit(ビット)と言っているわけですが、その bit の位置を左右にズラす事をシフトと呼んでいるのです。右にズラすと右シフト、左にズラすと左シフトです(当たり前)。
左シフトは << という演算子を使います。右シフトは >> です。なんとなく矢印記号の ← → に見えてきませんか。これがシフト演算子です。そんなイメージで覚えておいてください。記述方法は以下の通りです。
シフト演算子の右側の数字はシフト回数です。この数値指定には定数以外に変数も使用する事が出来ます。ただ、あまり大きい数字は使わないですね。
char bittest = 0xD9; bitL = bittest << 1; /* 左に1回シフトする */ bitR = bittest >> 2; /* 右に2回シフトする */
※ 回数に実数は使えません。
動作についてより細かく見ていきます。例えば 0xD9 という数値があるとします。この 0xD9 は 2進数に直すと 0b11011001 という表記になります。これを左シフトすると何が起きるのか。char 型で説明してみます。

このように右側から 0 が入ってきます。そして、左に押し出されます。

char 型は1バイト(8bit)のサイズしか保存できませんので、左側の bit が押し出されて消え失せます。結果、0b10110010 となります。16進数では 0xB2 に変わります。これがもし char 型ではなく int 型だと、VS2022 では 32bit を扱えるので、押し出された bit は変数内に残ります。そのため、int 型であれば 0xD9 を左シフトしたら 0x1B2 という数値になります。
右シフトも考え方は同じです。左から bit が入ってきて右に押し出されます。ただ、ちょっとだけ違うのは、左から入ってくる bit は 0 とは限らない点です。この動作は型が符号付きか、符号無し(unsigned 修飾子付き宣言)かと異なります。
先に2進数の中の特定の bit の呼び方を説明します。2進数では一番左側の bit を最上位ビット(MSB)と呼んでいます。逆に一番右側の bit を最下位ビット(LSB)と呼んでいます。
右シフトでは、符号付きの場合は MSB と同じ bit の値が入ってきます。符号なしの場合は必ず 0 が入ってきます。符号付きは値がマイナスの際は MSB が必ず 1 になっているため、このような動作になります。符号付きのメモリ内の状態は2の補数という状態で格納されています。これの説明は長くなるのと、直接的に C言語の説明とも異なるので、興味があればこちらをご覧ください。
2の補数 - Wikipedia
このシフトという動作はとても面白い性格を持っています。それは、左にシフトすると値は 2倍になるのです。そして、右シフトすると値は半分になるのです。まずは、数字の 5 を元に char 型でどんどん左シフトして確認してみます。2進数、10進数符号無し(unsigned char)、10進数符号あり(char)で確認してみます。※ この環境では char は符号付きと見なしています。
0b00000101 | 5 | 5 |
0b00001010 | 10 | 10 |
0b00010100 | 20 | 20 |
0b00101000 | 40 | 40 |
0b01010000 | 80 | 80 |
0b10100000 | 160 | -96 |
0b01000000 | 64 | 64 |
0b10000000 | 128 | -128 |
0b00000000 | 0 | 0 |
如何でしょうか。80 まではちゃんと2倍になっていますよね。そして、本来 160 となる時点で符号付き側はいきなり -96 と値が小さくなっています。これは、char 型が扱える数値の限度を超えたためです。符号無し char は 0 ~ 255、符号ありの場合は -128 ~ 127 が扱える範囲で、そこから逸脱した数値は正しく表現できないのです。
左にシフトした、つまり、数値を大きくした結果、扱える範囲を逸脱する事をオーバーフローと呼んでいます。整数の右シフトでは発生しませんが、四則演算の結果、扱える値が範囲を下回った場合をアンダーフローと呼んでいます。
このようなオーバーフローやアンダーフローのようなエラーは、現在のコンピュータで int で数値を扱っていると、初級の頃はあまり目にする事はないですが、実務レベルのプログラム演算では意識しないとハマる元になります。なので、char を文字以外に使う事はまずありません。特に理由がない限りは使わないのが無難です。
ソニー・インタラクティブエンタテインメント
※ 私が思うに、コントローラは結局は純正品が一番良いと思うんです。PS5 のコントローラは普通に買えるんですね。
- 四則演算の優先順位
この計算は、2 * 3 が先に計算されてから、その結果と + 1 計算されますので、7 という値になります。9 ではない点にご注意ください。優先順位が同じ場合は、右側から計算されていきます。例えば、こんな書き方が出来るのですが、
int num = 1 + 2 * 3;
最初に 3 * 4 が計算されて 12 になります。続いて、1 * 2 が計算されて 2、それが加算されるので、14 という結果が出来ます。それがまず n2 に格納されて、続いて n2 の内容が n1 に格納されます。
int n1, n2; n1 = n2 = 1 * 2 + 3 * 4;
これはまだ簡単なので、そうそう勘違いも無いかと思いますが、複雑な式になるとわかりにくい事があります。慣れないうちはこの演算の優先順位でバグを生む事もあるかと思います。シフト演算子が絡むとさらにわかりにくくなります。
その場合は、明示的に括弧を記述する事で、計算の優先度を明確にしたり計算優先度を変更できます。先の例ですが、括弧を…
このように付けると、2 + 3 が最優先で計算されて 5 が出来ます。その次に 4 * 5 * 1 と乗算されていって、20 という結果が変数に代入されます。
int n1, n2; n1 = n2 = 1 * (2 + 3) * 4;
- 複合代入演算子
この複合代入の書き方も、C言語では頻繁にお目にかかる代表的な記述方法なのです。では、まず例から。
このプログラムの動作はもう分かりますよね。n1 の 12 が、n2 の 3 で割られて、4
int n1 = 12; int n2 = 3; n1 = n1 / n2;
という結果になり、それがまた n1 に代入されます。このような、計算した結果をまた元の変数に入れるという動作を、簡略して記述できるのが複合代入演算子なのです。この書き方に変えてみます。
はい、とてつもなく見た事もないような書き方になりました(汗)。これは n1 に対して /(除算)を n2 と行って、その結果をまた n1 に入れるという動作をします。これはまだ分かりやすい方でしょうか。では、四則演算が混ざるとどうなるでしょうか。例えばこうです。
int n1 = 12; int n2 = 3; n1 /= n2;
動作は以下のどちらになると思いますか?
int n1 = 12; int n2 = 2; n1 /= n2 + 1;
- 12 ÷ 2 が実行されて 6 になってから +1 されるから 7 となる。
- 右辺の n2 + 1 が先に実行された 3 で 12 を割るので 4 となる。
この結果の予想をしてから、広告の下を見てください。※ そういや来月発売です(2022年8月現在)。もう予約は済んでます?
正解は 4 です。複合代入演算子は右辺の結果を左辺と計算する動作なんです。

複合代入演算子はほぼ全ての計算式に適用できます。単純な種類だけで言えば、ここまで説明してきただけでも +=, -=, *=, /=, %=, <<=, >>= が使えます。またインクリメントやデクリメントが混ざるとどうなるかは VS2022 でステップトレースで確認してみてください。例えばこんなプログラムで確認してみましょう。
#include <stdio.h> int main() { int n1, n2; n1 = 12; n2 = 2; n1 /= n2++; printf("後置インクリメントを含めた複合代入演算の結果は%dです。\n", n1); n1 = 12; n2 = 2; n1 /= ++n2; printf("前置インクリメントを含めた複合代入演算の結果は%dです。\n", n1); }

いや、ホントにインクリメントとデクリメントが混ざった動作は理解しづらいですね。でも、そのうち感覚的に理解できるようになります。大丈夫、あなたなら出来ます!
- (オマケ)ゼロ除算エラー
割る数にゼロが来る可能性がある場合は、必ずゼロかどうかを判断する処理が必要です。あるいはエラー処理を入れるか…ですが、これはどちらも先の話になってしまいます。今はゼロで割らないように気をつけるようにしてください。
- まとめ
- ±1 専用のインクリメントとデクリメントという演算子がある。
- インクリメントとデクリメントは記述する位置で動作が変わる。
- シフト演算子は bit の位置をズラす。
- 型の有効範囲からのはみ出しに注意する。
- 四則演算の優先順位が分かりにくい時は()で括る。
- 複合代入演算子も演算優先順位に注意する。
- ゼロ除算エラーに注意する。
>> C言語007 数値や文字を出力するに進む
>> C言語基礎講座インデックスに戻る
良かったらポチって頂けるとやる気が上がります!ご協力感謝です!!※ もしもコロナにかかって外出できなくなったらと考えた時、そういう非常食を買い揃えておく事は重要だと思い知りました。
コメント
コメント一覧 (10)
「n1++ は配置(こうち)インクリメントと呼びます」
→ 「後置」
「n1 /= n2;」の説明のところで
「という動作を染ます」
→ 「します」
内藤時浩
が
しました
内藤時浩
が
しました
>>bitR = bittest >> 2; /* 左に2回シフトする */
内藤時浩
が
しました
n3 = —n1++ / n2
みたいな使い方もできるのでしょうか?
コンパイラによる最適化がないと n1を一時的にでも書き換えるので気持ち悪いかもしれませんが。
内藤時浩
が
しました