演算子と式

演算子はキッチンの道具のようなものです——包丁で切り、フライパンで焼く。異なる材料には異なる道具が必要で、異なる計算には異なる演算子が必要です。

算術演算子

C言語には最も一般的な数学計算のための 7 つの基本算術演算子があります。

演算子 意味 結果
+ 加算 5 + 3 8
- 減算 5 - 3 2
* 乗算 5 * 3 15
/ 除算 7 / 2 3
% 剰余 7 % 2 1
+ 単項プラス +5 5
- 単項マイナス -5 -5

整数除算の落とし穴

2 つの整数を除算すると、結果も整数になります——小数部分は切り捨てられ、四捨五入ではありません。

C
#include <stdio.h>

int main(void) {
    int a = 7;
    int b = 2;
    printf("%d / %d = %d\n", a, b, a / b);
    printf("%d %% %d = %d\n", a, b, a % b);
    return 0;
}
TEXT
7 / 2 = 3
7 % 2 = 1
💡 ヒント: 小数を含む結果を得るには、少なくとも一方のオペランドを浮動小数点にします:7.0 / 2 なら 3.5 になります。

剰余演算子の用途

剰余演算子 % は整数にしか使えません。実用的な使い方がいくつかあります:

C
#include <stdio.h>

int main(void) {
    int n = 12345;
    printf("末尾の桁: %d\n", n % 10);
    printf("偶数か: %s\n", n % 2 == 0 ? "はい" : "いいえ");
    return 0;
}
TEXT
末尾の桁: 5
偶数か: いいえ

インクリメント・デクリメント演算子

インクリメント演算子 ++ は変数に 1 を加え、-- は 1 を引きます。それぞれ前置と後置の 2 つの形式があり、式が返す値が異なります。

前置と後置

形式 名称 効果 式の値
++i 前置インクリメント i が 1 増える 新しい(増えた)値
i++ 後置インクリメント i が 1 増える 古い(元の)値
--i 前置デクリメント i が 1 減る 新しい(減った)値
i-- 後置デクリメント i が 1 減る 古い(元の)値
C
#include <stdio.h>

int main(void) {
    int a = 5;
    int b = ++a;
    printf("前置: a=%d, b=%d\n", a, b);

    int c = 5;
    int d = c++;
    printf("後置: c=%d, d=%d\n", c, d);
    return 0;
}
TEXT
前置: a=6, b=6
後置: c=6, d=5
⚠️ 注意: 同一の変数に対し、1 つの式の中で ++ や -- を複数回使わないでください。a = ++a + a++ のような書き方は未定義動作で、コンパイラによって結果が異なります。

関係演算子

関係演算子は 2 つの値を比較し、「真」(1)または「偽」(0)を返します。

演算子 意味 結果
== 等しい 5 == 5 1
!= 等しくない 5 != 3 1
< より小さい 3 < 5 1
> より大きい 3 > 5 0
<= 以下 5 <= 5 1
>= 以上 3 >= 5 0
C
#include <stdio.h>

int main(void) {
    int a = 10;
    int b = 20;
    printf("a == b: %d\n", a == b);
    printf("a != b: %d\n", a != b);
    printf("a < b:  %d\n", a < b);
    return 0;
}
TEXT
a == b: 0
a != b: 1
a < b:  1
⚠️ 注意: == は比較、= は代入です。書き間違えると比較が代入になり、エラーも出ません。良い習慣として定数を左側に置きましょう:5 == a と書けば、うっかり 5 = a と書いてもコンパイラが検出してくれます。

論理演算子

論理演算子は複数の条件を組み合わせ、やはり 1(真)または 0(偽)を返します。

演算子 意味 結果
&& 論理積(AND) 1 && 0 0
` ` 論理和(OR)
! 論理否定(NOT) !0 1

真理値表

| A | B | A && B | A || B | !A | |---|---|--------|--------|----| | 0 | 0 | 0 | 0 | 1 | | 0 | 1 | 0 | 1 | 1 | | 1 | 0 | 0 | 1 | 0 | | 1 | 1 | 1 | 1 | 0 |

短絡評価

&& では左側が偽なら右側は評価されません。|| では左側が真なら右側はスキップされます。この機能を利用して不正な操作を防げます:

C
#include <stdio.h>

int main(void) {
    int divisor = 0;
    int value = 10;
    if (divisor != 0 && value / divisor > 5) {
        printf("条件を満たしました\n");
    } else {
        printf("条件を満たしません。ゼロ除算も発生していません\n");
    }
    return 0;
}
TEXT
条件を満たしません。ゼロ除算も発生していません
💡 ヒント: C言語では 0 が偽、0 以外の値が真です(負の数も含みます)。したがって -5 は論理的には「真」です。

代入演算子

基本の代入演算子 = は右辺の値を左辺の変数に格納します。C言語には演算と代入を組み合わせた複合代入演算子もあります。

複合 同等の式
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
C
#include <stdio.h>

int main(void) {
    int score = 80;
    score += 10;
    printf("ボーナス後: %d\n", score);
    score -= 25;
    printf("ペナルティ後: %d\n", score);
    score *= 2;
    printf("倍増後: %d\n", score);
    return 0;
}
TEXT
ボーナス後: 90
ペナルティ後: 65
倍増後: 130

条件演算子(三項演算子)

条件演算子 ?: は C言語唯一の三項演算子です。構文は次の通り:

TEXT
条件 ? 式1 : 式2

条件が真なら式1が評価され、偽なら式2が評価されます。if-else の短縮形です。

C
#include <stdio.h>

int main(void) {
    int age = 20;
    char *status = age >= 18 ? "成人" : "未成年";
    printf("年齢 %d、%s\n", age, status);

    int a = 15, b = 28;
    int max = a > b ? a : b;
    printf("大きい値: %d\n", max);
    return 0;
}
TEXT
年齢 20、成人
大きい値: 28

カンマ演算子

カンマ演算子 , は各式を左から右へ評価し、カンマ式全体の値は最後の式の値になります。

C
#include <stdio.h>

int main(void) {
    int a, b, c;
    c = (a = 1, b = 2, a + b);
    printf("a=%d, b=%d, c=%d\n", a, b, c);
    return 0;
}
TEXT
a=1, b=2, c=3
⚠️ 注意: カンマ演算子はすべての演算子の中で最も低い優先順位を持ちます。関数の引数間のカンマとカンマ演算子を混同しないでください——引数間のカンマはカンマ演算子ではありません。

sizeof演算子

sizeof はデータ型や変数がメモリ上で占めるバイト数を返します。コンパイル時に評価され、実行時ではありません。

C
#include <stdio.h>

int main(void) {
    printf("char:   %zu bytes\n", sizeof(char));
    printf("int:    %zu bytes\n", sizeof(int));
    printf("float:  %zu bytes\n", sizeof(float));
    printf("double: %zu bytes\n", sizeof(double));

    int arr[5] = {1, 2, 3, 4, 5};
    printf("配列の合計サイズ: %zu bytes\n", sizeof(arr));
    printf("配列の要素数: %zu\n", sizeof(arr) / sizeof(arr[0]));
    return 0;
}
TEXT
char:   1 bytes
int:    4 bytes
float:  4 bytes
double: 8 bytes
配列の合計サイズ: 20 bytes
配列の要素数: 5
💡 ヒント: 型名には括弧が必要(sizeof(int))ですが、変数には不要(sizeof a)です。ただし一貫性のため、常に括弧を使うのが良いでしょう。

演算子の優先順位

式に複数の演算子が含まれる場合、優先順位が高いものが先に評価されます。下の表は優先順位が高い順に並べています:

優先順位 演算子 結合性
1(最高) () [] -> . 左から右
2 ! ~ ++ -- +(単項) -(単項) *(間接参照) &(アドレス取得) sizeof 右から左
3 * / % 左から右
4 + - 左から右
5 << >> 左から右
6 < <= > >= 左から右
7 == != 左から右
8 & 左から右
9 ^ 左から右
10 ` `
11 && 左から右
12 `
13 ?: 右から左
14 = += -= *= /= %= など 右から左
15(最低) , 左から右
🔥 よくある間違い: 優先順位表を暗記できませんか?無理に覚える必要はありません。迷ったら括弧を追加しましょう。括弧は常に最優先であり、コードも明確になります。

各種演算子を組み合わせて 3 桁の数値の各桁の合計を計算します:

C
#include <stdio.h>

int main(void) {
    int num = 952;
    int hundreds = num / 100;
    int tens = (num / 10) % 10;
    int ones = num % 10;
    int sum = hundreds + tens + ones;

    printf("数値: %d\n", num);
    printf("百の位: %d、十の位: %d、一の位: %d\n", hundreds, tens, ones);
    printf("各桁の合計: %d\n", sum);
    return 0;
}
▶ 試してみよう
TEXT
数値: 952
百の位: 9、十の位: 5、一の位: 2
各桁の合計: 16

論理演算子と関係演算子を使って閏年かどうかを判定します:

C
#include <stdio.h>

int main(void) {
    int year = 2024;
    int is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);

    printf("%d 年は閏年%s\n", year, is_leap ? "です" : "ではありません");
    return 0;
}
▶ 試してみよう
TEXT
2024 年は閏年です

型変換

暗黙の変換

異なる型が混在する式では、コンパイラが自動的に精度の低い型を精度の高い型に変換します:

C
#include <stdio.h>

int main(void) {
    int a = 5;
    double b = 2.0;
    double c = a / b;
    printf("5 / 2.0 = %f\n", c);

    char ch = 'A';
    int code = ch + 1;
    printf("'A' + 1 = %c(ASCII %d)\n", code, code);
    return 0;
}
TEXT
5 / 2.0 = 2.500000
'A' + 1 = B(ASCII 66)

変換の方向:charintlongdoubleintunsignedlongdouble

明示的キャスト

(型名) を使って値を強制的に特定の型に変換します:

C
#include <stdio.h>

int main(void) {
    int a = 7, b = 2;
    double result = (double)a / b;
    printf("キャスト付き: %f\n", result);
    return 0;
}
TEXT
キャスト付き: 3.500000
⚠️ 注意: キャストは元の変数の値や型を変更しません——一時的な新しい値を生成するだけです。

❓ よくある質問

Q 単独で使う場合、a++++a に違いはありますか?
A ありません。違いが生じるのは式の中で返り値を使う場合だけです。単独で使うと効果は同じですが、理論上は前置インクリメントの方がわずかに効率的です。
Q === をどう区別すればいいですか?
A = は代入(「になる」)、== は比較(「等しいか?」)です。if (a = 0) と書き間違えてもエラーになりませんが、ロジックは完全に変わります。良い習慣:定数を左側に置く——if (0 == a) と書けば、if (0 = a) とタイプミスしてもコンパイラが検出してくれます。
Q % 演算子は浮動小数点数にも使えますか?
A いいえ。% は整数型にしか使えません。浮動小数点の剰余には #include <math.h>fmod() 関数を使ってください。
Q 演算子の優先順位表をすべて暗記する必要がありますか?
A いいえ。乗算・除算・剰余が加算・減算より先、論理 NOT が AND より先、AND が OR より先ということだけ覚えておけば十分です。その他は括弧を追加すれば安全です。

📖 まとめ

📝 練習問題

  1. 秒数(例:3661)を入力とし、/% を使って対応する時間・分・秒を計算して出力するプログラムを書いてください
  2. 2 つの整数を入力とし、三項演算子を使って大きい値と小さい値を見つけて出力するプログラムを書いてください
  3. 自分のパソコンで shortintlonglong long のサイズを sizeof で出力し、結果を観察するプログラムを書いてください
100%