C言語の演算子をマスターしよう!プログラミング初心者向け完全ガイド

この記事のポイント

C言語の演算子は、プログラムを作る上で欠かせない基本的な要素です。

この記事を読むと、次のようなことがわかります。

  • 演算子の基本的な種類と使い方
  • 演算子を使う際の注意点とミスしやすいポイント
  • 演算子の優先順位や順番の大切さ
  • 複合代入演算子やビット演算子といった効率的なコードの書き方

演算子は、プログラミングの基礎中の基礎です。足し算や引き算といった単純な計算から、複雑な条件判定まで、あらゆる場面で演算子を使います。

この記事を読み終わる頃には、C言語でのデータ操作や条件判定ができるようになっているはずです。

ぜひこの機会に、演算子をマスターしてしまいましょう。

目次

演算子とは

演算子は、データを操作したり計算したりするための記号です。

プログラムを作る上で、「数値を計算する」「条件を判定する」「データを比較する」といった処理は欠かせません。

つまり、その処理に使う演算子も必須であるということです。

演算子を正しく理解すると、効率的で読みやすいコードが書けるようになるでしょう。

【関連】
C言語をもっと詳しく学ぶならpaizaラーニング

演算子の基本概念とオペランドの関係

演算子は、計算や処理を行う記号のことで、その対象となるデータをオペランドと呼びます。

例えば、「a + b」という式を見てみましょう。この場合、「+」が演算子で、「a」と「b」がオペランドです。

演算子とオペランドを組み合わせることで、さまざまな処理を実現できます。

#include <stdio.h> int main() { int cat = 5; int dog = 3; int total = cat + dog; printf("%d\n", total); return 0; }

出力結果

8

このプログラムでは、catとdogという2つの変数(オペランド)に数値が入っています。+演算子を使って、この2つの値を足し算し、結果をtotalに格納しています。

演算子の種類一覧

演算子は、用途に応じて分類されています。

主な演算子の種類と役割をまとめました。

  • 算術演算子(+、-、*、/、%):数値の計算に使う
  • 代入演算子(=、+=、-=など):変数に値を設定したり更新したりする
  • 比較演算子(==、!=、<、>など):値の大小や等しさを判定する
  • 論理演算子(&&、||、!):複数の条件を組み合わせる
  • ビット演算子(&、|、^、~、<<、>>):より高度なデータ操作を行う

演算子にはそれぞれ役割があります。必要な場面で適した演算子を使うことができるように、しっかりと覚えておきましょう。

#include <stdio.h> int main() { int rabbit = 10; int mouse = 5; int result = (rabbit > mouse) && (rabbit != 0); printf("%d\n", result); return 0; }

出力結果

1

この例では、比較演算子(>と!=)と論理演算子(&&)を組み合わせています。

rabbit > mouseは「rabbitの方が大きいか」を判定し、rabbit != 0は「rabbitが0でないか」を判定します。

&&で両方の条件をつなぎ、「両方とも真(正しい)」の場合に1(真)が返されているという形です。

ちなみに、正しい(真) 場合は1になり、正しくない(偽) のときには0になります。

演算子が使われる主な場面

演算子は、日常的なプログラミングのあらゆる場面で活用されます。

例えば、演算子を使うのは次のような場面です。

  • 四則演算:数値の計算処理を行うとき
  • 条件分岐if文で使う判定条件を設定するとき
  • ループ処理:繰り返し条件の評価や、カウンター変数の更新時

どれもプログラムの根幹をなす挙動といっても過言ではありません。

実際の例を見てみましょう。

#include <stdio.h> int main() { for (int tiger = 0; tiger < 3; tiger++) { printf("Tiger count: %d\n", tiger + 1); } return 0; }

出力結果

Tiger count: 1
Tiger count: 2
Tiger count: 3

for文の中で複数の演算子を使っているのが確認できますね。

tiger < 3は比較演算子で「tigerが3より小さいか?」を判定し、tiger++はインクリメント演算子でtigerの値を1ずつ増やします。

また、tiger + 1は算術演算子で値に1を足しています。このように、演算子を組み合わせることで、繰り返し処理を実現できます。

ちなみに、インクリメント演算子 (++) は、プログラミングにおいて変数の値を1増やすための専用の演算子です。

これは、変数 = 変数 + 1; と書くのと同じ処理を、より短く、効率的に行うための演算子です。

逆に、デクリメント演算子 (--) という変数の値を1減らす演算子もあります。

役割としては算術演算子と同じであるため、先述した種類の一覧からは省きましたが、実際にコードを書く際には頻繁に用います。

算術演算子

算術演算子は、数値計算の役割を持った演算子です。プログラミングで計算処理を行う上での土台となります。

足し算、引き算、掛け算、割り算、余りの計算など、基本的な計算処理を行うことができます。

加減乗除と剰余の基本構文

基本的な算術演算子には、次の5つの種類があります。

  • 加算(+):2つの数を足す
  • 減算(-):2つの数を引く
  • 乗算(*):2つの数を掛ける
  • 除算(/):2つの数を割る
  • 剰余(%):割り算の余りを求める

数学の四則演算に対応しているので、直感的に使用できるはずです。

剰余演算子は割り算の余りを求める際に使い、偶数奇数の判定でよく活用されます。

#include <stdio.h> int main() { int sheep = 15; int goat = 4; printf("Sum: %d, Difference: %d\n", sheep + goat, sheep - goat); printf("Product: %d, Remainder: %d\n", sheep * goat, sheep % goat); return 0; }

出力結果

Sum: 19, Difference: 11
Product: 60, Remainder: 3

いくつもの算術演算子を使って、基本的な計算をしているのが確認できると思います。

剰余演算子%は、15を4で割ったときの余り(3)を返しています。

整数同士の割り算と注意点

整数同士の除算では、結果も整数になるため小数部分が切り捨てられます

例えば、7を3で割った場合、数学的には2.333...ですが、C言語では2になるということです。

小数を含む結果を得たい場合は、どちらか一方をfloatやdouble型にキャスト(型変換)する必要があります。
この特性を理解せずに使うと、予期しない結果になってしまいます。

実際の例を見てみましょう。

#include <stdio.h> int main() { int horse = 7; int cow = 3; printf("Integer division: %d\n", horse / cow); printf("Float division: %.2f\n", (float)horse / cow); return 0; }

出力結果

Integer division: 2
Float division: 2.33

最初のhorse / cowでは、整数同士の割り算なので、結果は2になります。なぜなら、小数部分が切り捨てられるからです。

次の(float)horse / cowでは、horseをfloat型にキャストしているため、浮動小数点を含んだ計算となり、2.33という小数を含む結果が得られます。

ちなみに、%.2fは小数点以下2桁まで表示する書式指定子です。

代入・複合代入演算子

代入演算子は、変数に値を入れるための役割を持ちます。

プログラムの状態を管理したり、データを操作したりするために欠かせません。 

単純に値を代入するだけでなく、計算と代入を同時に行う複合代入演算子もあります。この使い分けができるようになると、簡潔で読みやすいコードが書けるようになるでしょう。

基本の代入演算子「=」の役割

代入演算子「=」は、右辺の値を左辺の変数に格納する働きをします。

数学の等号(イコール)とは意味が異なるので、注意しましょう。

数学では「等しい」という意味ですが、プログラミングでは「右から左への値の移動」を表現しています。

  • 左辺には必ず変数を指定する
  • 右辺には値や式を記述する

このルールを守ることで、正しくデータを扱えるようになります。

#include <stdio.h> int main() { int lion = 10; int bear; bear = lion; printf("Lion: %d, Bear: %d\n", lion, bear); return 0; }

出力結果

Lion: 10, Bear: 10

この例では、まずlionという変数に10という値を代入しています。次にbear = lion;で、lionの値(10)をbearに代入しているのが確認できますね。

複合代入演算子の活用例

複合代入演算子は、演算と代入を一つの式で表現できる便利な機能です。

例えば、a += bはa = a + bと同じ意味を持ちます。これにより、コードを簡潔にできます。

主な複合代入演算子を確認しましょう。

  • +=:加算して代入(a += bはa = a + bと同じ)
  • -=:減算して代入(a -= bはa = a - bと同じ)
  • *=:乗算して代入(a *= bはa = a * bと同じ)
  • /=:除算して代入(a /= bはa = a / bと同じ)
  • %=:剰余を求めて代入(a %= bはa = a % bと同じ)

繰り返し処理やカウンターの更新でよく使うので、覚えておきましょう。

#include <stdio.h> int main() { int fox = 5; fox += 3; printf("After += 3: %d\n", fox); fox *= 2; printf("After *= 2: %d\n", fox); return 0; }

出力結果

After += 3: 8
After *= 2: 16

代入と演算の順序に注意

複数の代入や演算が含まれる式では、順番が大事です。

代入演算子は右結合性を持つため、右から左に評価されるという特徴があります。

また、インクリメント演算子(++)やデクリメント演算子(--)との組み合わせでは、前置と後置の違いによって結果が変わることがあります。

前置と後置の違い

  • 前置(++wolf):先に値を増やしてから、その値を使う
  • 後置(wolf++):現在の値を使ってから、後で値を増やす

予期しない副作用を避けるため、複雑な式では適切に括弧を使用しましょう。

#include <stdio.h> int main() { int wolf = 5; int eagle = ++wolf; printf("Wolf: %d, Eagle: %d\n", wolf, eagle); return 0; }

出力結果

Wolf: 6, Eagle: 6

++wolfという前置インクリメントを使っています。前置の場合、まずwolfの値を1増やし、6にした後にその値をeagleに代入します。

では、もしwolf++の場合はどうなるでしょうか?

実際に試してみてください。

比較・論理演算子

プログラムで「もし〜なら、〜する」という判断をする際に使うのが、比較演算子と論理演算子です。

比較演算子は、2つの値を比べて「どちらが大きいか」「等しいか」などを調べます。

論理演算子は、複数の条件を「〜かつ〜」「〜または〜」のように組み合わせることができます。

if文やwhile文の条件判定には必須です。

比較演算子の使い方と注意点

比較演算子を使うと、2つの値の関係を調べることができます。

比較演算子には次のような種類があります。

  • ==:左右が等しい
  • !=:左右が等しくない
  • <:左が右より小さい
  • >:左が右より大きい
  • <=:左が右以下
  • >=:左が右以上

等しいかどうかを調べるときは==を使います。

=は代入なので、if文の条件で=を使ってしまうと、比較ではなく値の代入が行われてしまいます。これは非常によくあるミスです。

また、小数(float型やdouble型)の比較では、計算誤差により完全に等しいかどうかの判定が難しい場合があります。
小数点以下に誤差が生じるケースがあるからです。

この2つの注意点は覚えておきましょう。

#include <stdio.h> int main() { int panda = 5; int koala = 3; if (panda == koala) { printf("Same value\n"); } else { printf("Different values: %d and %d\n", panda, koala); } return 0; }

出力結果

Different values: 5 and 3

panda == koalaで、2つの変数の値が等しいかを調べています。

5と3は等しくないので、条件は偽(false)となり、else節の「Different values: 5 and 3」が表示されるという形です。

もし=を使ってしまうと、pandaにkoalaの値(3)が代入されてしまいます。

論理演算子による条件の組み合わせ

論理演算子を使うと、複数の条件を組み合わせて判定できます。

論理演算子の種類

  • &&(論理積、AND):両方の条件が真の場合だけ真になる
  • ||(論理和、OR):どちらか一方でも真であれば真になる
  • !(論理否定、NOT):真偽を反転させる

論理演算子には短絡評価という特性があります。

&&では左側が偽(false)なら右側は評価されません。
||では左側が真(true)なら右側は評価されません。

結果が確定した時点で残りの条件はチェックしないため、処理が効率的になるというメリットがあります。

#include <stdio.h> int main() { int monkey = 8; int gorilla = 12; if (monkey > 5 && gorilla < 15) { printf("Both conditions are true\n"); } return 0; }

出力結果

Both conditions are true

monkey > 5は真(8は5より大きい)で、gorilla < 15も真(12は15より小さい)です。&&は両方が真の時に真になるので、条件全体が真となっています。

反転演算子の活用法

論理否定演算子(!)を使うと、条件の真偽を逆にすることができます。

「〜でない場合」という条件を表現したいときや、フラグの状態を切り替える際に便利です。

ただし、複雑な条件で何度も!を使うと読みにくくなるため、適切に括弧を使ってわかりやすく書くことが大切です。

実際の例を見てみましょう。

#include <stdio.h> int main() { int penguin = 0; if (!penguin) { printf("Penguin is false (zero)\n"); } penguin = 1; if (penguin) { printf("Penguin is true (non-zero)\n"); } return 0; }

出力結果

Penguin is false (zero)
Penguin is true (non-zero)

C言語では、0は偽(false)、0以外の値は真(true)として扱われます。

最初の!penguinは、penguinが0(偽)なので、!で反転して真になっています。

ビット演算子の基礎

ビット演算子は、データを2進数(0と1の並び)として扱い、ビット単位で操作する演算子です。

初心者の方には少し難しく感じられるかもしれませんが、基本的な考え方を理解すれば、メモリを効率的に使ったり、高速な処理を実現したりできるようになります。システムプログラミングや組み込み開発では頻繁に使われる技術です。

まずは基本概念から学んでいきましょう。

ちなみに、この項では2進数の理解が必須です。この機会に、2進数の考え方をよく理解しておきましょう。

ビット演算の基本概念

ビット演算を理解するには、コンピュータが数値を2進数で扱っているという点を理解する必要があります。

2進数の例

  • 5は2進数で「101」
  • 3は2進数で「011」

ビット演算は、この2進数の各桁(ビット)に対して論理演算を行います。例えば、5(101₂)と3(011₂)のビット積(&)を行うと、各ビット位置で論理積が計算されます。

実際の例を見てみましょう。

#include <stdio.h> int main() { int dolphin = 5; int whale = 3; int result = dolphin & whale; printf("5 & 3 = %d (binary: %d)\n", result, result); return 0; }

出力結果

5 & 3 = 1 (binary: 1)

dolphin & whaleで、5と3のビット積を計算しています。
各ビット位置で次のように論理積を計算しています。

1桁目:1 & 1 = 1
2桁目:0 & 1 = 0
3桁目:1 & 0 = 0

結果は「001」で、10進数では1になるというわけです。

チルダ演算子によるビット反転

ビット反転演算子(~)は、すべてのビットを反転させます。
0は1に、1は0になるということです。

注意が必要なのは、符号ビット(数値が正か負かを示すビット)も反転するという点です。そのため、正の数に~を使うと負の数になります。

例えば、 5(00000101)に~を使うと、すべてのビットが反転します。32ビット環境では、符号ビットも反転するため、結果は-6になります。

実際の例を見てみましょう。

#include <stdio.h> int main() { int shark = 5; int inverted = ~shark; printf("Original: %d, Inverted: %d\n", shark, inverted); return 0; }

出力結果

Original: 5, Inverted: -6

5のビットを反転すると-6になります。

これは、2の補数表現というコンピュータの仕組みによるものです。反転演算子は、主にマスク処理や特定のビットパターンの生成に使われます。

XOR(排他的論理和)演算子の特徴

XOR演算子(^)は、対応するビットが異なる場合に1、同じ場合に0を返します。この特性により、同じ値で2回XOR演算を行うと元の値に戻るという面白い性質があります。

データの暗号化や、2つの変数の値を一時変数なしで交換するといったテクニックで使われます。

#include <stdio.h> int main() { int octopus = 6; int squid = 3; int xor_result = octopus ^ squid; printf("6 ^ 3 = %d\n", xor_result); printf("Result ^ 3 = %d\n", xor_result ^ squid); return 0; }

出力結果

6 ^ 3 = 5
Result ^ 3 = 6

最初のoctopus ^ squidで6と3のXORを計算し、結果は5になります。

1桁目:0 ^ 1 = 1(異なる)
2桁目:1 ^ 1 = 0(同じ)
3桁目:1 ^ 0 = 1(異なる)

次にxor_result ^ squidで、5と3のXORを計算すると、元の値6に戻ります。この「2回XORすると元に戻る」という性質が、XORの最大の特徴です。

ビットシフト演算子の使い方

ビットシフト演算子は、ビットパターンを左右に移動させます。

  • 左シフト(<<):ビットを左に移動し、空いた右側は0で埋める
  • 右シフト(>>):ビットを右に移動する

左シフトは2の累乗倍の計算と同じ効果があります。

例えば、4 << 2は4を左に2ビットシフトするので、4 × 2² = 16になるということです。

右シフトは2の累乗で割るのと同じ効果があります。掛け算や割り算より高速に計算できるため、パフォーマンスが重要な場面で有用です。

#include <stdio.h> int main() { int turtle = 4; printf("Original: %d\n", turtle); printf("Left shift by 2: %d\n", turtle << 2); printf("Right shift by 1: %d\n", turtle >> 1); return 0; }

出力結果

Original: 4
Left shift by 2: 16
Right shift by 1: 2

4(100₂)を左に2ビットシフトすると10000₂(16)になります。これは4 × 4 = 16と同じ結果です。

右に1ビットシフトすると10₂(2)になります。これは4 ÷ 2 = 2と同じ結果です。このように、ビットシフトは効率的な計算方法として活用できます。

演算子の優先順位と評価順序の理解

複数の演算子が含まれる式では、どの演算が先に実行されるかを決める優先順位が存在します。

数学で「掛け算は足し算より先に計算する」というルールがあるのと同じように、C言語の演算子にも優先順位があります。

予期しないバグを防ぐためにも、正しい優先順位を認識しておきましょう。

演算子の優先順位

演算子には、それぞれ優先度が決められています。

優先順位の基本的な順序(高い順)

  1. 括弧、配列の添字など
  2. 単項演算子(++、--、!など)
  3. 乗算・除算・剰余(*、/、%)
  4. 加算・減算(+、-)
  5. 比較演算子(<、>、<=、>=)
  6. 等価演算子(==、!=)
  7. 論理積(&&)
  8. 論理和(||)
  9. 代入演算子(=、+=など)

例えば、a + b > cという式があったとします。

算術演算子は比較演算子より優先度が高いため、まずa + bが計算され、その結果がcと比較されます。つまり、(a + b) > cとして評価されるわけです。

実際の例を見てみましょう。

#include <stdio.h> int main() { int zebra = 5; int giraffe = 3; int elephant = 2; int result = zebra + giraffe * elephant; printf("5 + 3 * 2 = %d\n", result); return 0; }

出力結果

5 + 3 * 2 = 11

数学と同じように、掛け算(*)は足し算(+)より優先されます。

そのため、まずgiraffe * elephant(3 × 2 = 6)が先に計算され、次にzebra + 6(5 + 6 = 11)が計算されるという形です。

評価順序と副作用に注意

優先順位とは別に、気を付けなければいけないのが「評価順序」と「副作用」です。

例えば、先述したインクリメント演算子には、次のような性質がありましたね。

  • 前置(++i):変数を先に増やし、その値を使う
  • 後置(i++):現在の値を先に使った後に、変数を増やす

単独で使う場合(i++;だけの文)は結果は同じですが、式の中で使うと結果が変わります。

実際の例を見てみましょう。

#include <stdio.h> int main() { int kangaroo = 5; printf("Pre-increment: %d\n", ++kangaroo); kangaroo = 5; printf("Post-increment: %d\n", kangaroo++); printf("Final value: %d\n", kangaroo); return 0; }

出力結果

Pre-increment: 6
Post-increment: 5
Final value: 6

前置か後置かによって、挙動が異なっているのが確認できるかと思います。

後置の場合は、先に今の値(5)を表示してから、後でkangarooを6に増やします。そのため、最後の値は6になります。

ちなみに、関数の引数リストで複数の式を評価する順序は、C言語の規格では決められていません。

そのため、副作用のある式(変数の値を変える式)を複数使うと、環境によって結果が変わる恐れがあります。

例えば、次のようなコードは極力避けるべきです。

print_values(i++, i++); 

安全なコードを書くためには、副作用のある式は単独の文で先に実行し、結果を別の引数として渡すのが確実な方法です。

かっこを使った明示的な順序指定

演算子の優先順位を完璧に覚える必要はありません。迷った際は括弧を使いましょう。

それ以外にも次のようなメリットがあります。

  • 計算順序が一目でわかる
  • 他の人がコードを読んだときに意図が理解しやすい
  • 予期しない計算順序によるバグを防げる

特にチームで開発する場合、括弧を使って計算順序を明確にすることは、良いプログラミング習慣だといえます。

自分が優先順位を理解していても、他の人が読んだときにわかりやすいコードを書くことが大切です。

#include <stdio.h> int main() { int hippo = 10; int rhino = 6; int cheetah = 2; printf("Without parentheses: %d\n", hippo - rhino / cheetah); printf("With parentheses: %d\n", (hippo - rhino) / cheetah); return 0; }

出力結果

Without parentheses: 7
With parentheses: 2

ここまで読み進めたあなたなら、演算子にとって順番がどれほど大事か認識できていると思います。

適切に括弧を使うことができるようになりましょう。

よくある質問(Q&A)

Q: 代入演算子=と比較演算子==の違いは? 

A: =は右辺の値を左辺の変数に格納する代入操作で、==は左右の値が等しいかを判定する比較操作です。if文では必ず==を使用します。誤って用いると予期しない動作になるため、注意しましょう。

Q: 整数の割り算で小数が欲しい場合は? 

A: 整数同士の除算は結果も整数になり小数部分が切り捨てられます。小数結果を得るには、どちらか一方を(float)でキャストしてから除算を行いましょう。これにより浮動小数点演算となり、小数部分も保持されます。

Q: ++演算子の前置と後置の使い分けは? 

A: 前置(++a)は変数を先にインクリメントしてからその値を使用し、後置(a++)は現在の値を使用してから後でインクリメントします。単独で使用する場合は動作は同じですが、式の中で使う場合は結果が異なるので、注意しましょう。

Q: ビット演算子はいつ使うの? 

A: ビット演算子は主にシステムプログラミングや組み込み開発で使用されます。フラグ管理、高速な2の累乗による乗除算、特定のビット操作が必要な場面で威力を発揮します。

初心者の段階では、基本概念を理解しておけば十分です。

Q: 複雑な式の計算順序がわからないときは? 

A: 演算子の優先順位を完全に覚える必要はありません。計算順序が不明な場合は、積極的に括弧を使用して明示的に指定しましょう。これによりコードの可読性も向上し、意図しないバグを防げます。

まとめ

C言語の演算子は、プログラミングの基礎です。データの操作や制御フローの実現には欠かせません。

この記事では、演算子の基本的な使い方から、実際に使う際の注意点、そして優先順位まで解説してきました。

演算子が特に有用なのは、次のような場面です。

演算子が活躍する場面

  • 数値計算や文字列処理などを行いたいとき
  • データを操作したいとき
  • if文やwhile文で条件判定を行いたいとき
  • 変数の値を更新したり、カウンターを増減させたいとき
  • ビット単位でデータを効率的に処理したいとき

最後に、重要なポイントをおさらいしましょう。

重要なポイント

  • 算術、代入、比較、論理、ビットの5つのカテゴリに分類される
  • =は代入、==は比較という違いがある
  • 整数同士の割り算は小数部分が切り捨てられる
  • 複合代入演算子(+=、-=など)を使うと、コードが簡潔になる
  • 演算子の優先順位を理解し、迷ったら括弧を使う

演算子を正しく理解して使うための最も大切なことは、実際にコードを書いて試してみることです。

特に、最初のうちは=と==を間違えたり、整数の割り算で小数が出ないことに戸惑ったりすることもあるかもしれません。

慣れてくると、自然に使い分けられるようになります。

演算子は、プログラミングの土台となる基本中の基本です。数値計算、条件分岐、ループ処理など、ほぼすべてのプログラムで演算子を使います。

この記事で学んだ基礎をしっかり身に付けて、より複雑なプログラムも作れるようになりましょう。

レベルを更に上げたい方はpaizaプログラミングスキルチェックへ

  1. paizaラーニングトップ
  2. ナレッジ
  3. C言語のナレッジ記事一覧
  4. C言語の演算子をマスターしよう!プログラミング初心者向け完全ガイド