C言語で条件分岐や繰り返しを正しく書くためには、比較演算子と論理演算子の理解が欠かせません。
本記事では、>、<、==、!=、&&、||、!の基本から、短絡評価や優先順位、よくある初学者のつまずきまでを、サンプルコードと出力例つきで丁寧に解説します。
0と非0による真偽の扱いや= と == の取り違えといった重要ポイントも具体例で確認します。
C言語の比較演算子と論理演算子の基本
C言語の真偽値(0/非0)
C言語では、条件式の結果は整数の0(偽)または非0(真)として扱われます。
比較演算や論理演算の評価結果も数値として0または1になります。
変数そのものを条件に使った場合は0なら偽、0以外なら真として評価されます。
// 0と非0の評価、および比較演算の結果は0/1になる例
#include <stdio.h>
int main(void) {
int a = 0;
int b = 42;
int c = -3;
// 変数を条件として解釈するなら「!= 0」で真偽に正規化して見ると分かりやすい
printf("a を条件にした評価(a != 0): %d\n", a != 0); // 0 -> 偽
printf("b を条件にした評価(b != 0): %d\n", b != 0); // 1 -> 真
printf("c を条件にした評価(c != 0): %d\n", c != 0); // 1 -> 真
// 比較演算の結果も0/1
printf("3 < 5: %d\n", 3 < 5);
printf("3 > 5: %d\n", 3 > 5);
printf("3 == 3: %d\n", 3 == 3);
printf("3 != 3: %d\n", 3 != 3);
return 0;
}
a を条件にした評価(a != 0): 0
b を条件にした評価(b != 0): 1
c を条件にした評価(c != 0): 1
3 < 5: 1
3 > 5: 0
3 == 3: 1
3 != 3: 0
論理演算の結果や比較演算の結果が整数であるという点は、printf("%d", 条件)などで確認するのに便利です。
if文での条件の評価
ifは括弧内の式が0以外なら真、0なら偽として分岐します。
変数をそのまま条件に使うこともできます。
// if文における0/非0の評価
#include <stdio.h>
int main(void) {
int x = 0;
if (x) {
printf("if(x): xは真です\n");
} else {
printf("if(x): xは偽です\n");
}
x = -5;
if (x != 0) { // 明示的に比較しても同じ意味
printf("if(x != 0): xは非0なので真です\n");
}
return 0;
}
if(x): xは偽です
if(x != 0): xは非0なので真です
比較演算子(>, <, ==, !=)の使い方
>(大なり)と<(小なり)の基本
>や<は大小関係を判定します。
結果は0または1です。
// > と < の基本
#include <stdio.h>
int main(void) {
int x = 7;
int y = 10;
printf("x(7) > y(10): %d\n", x > y);
printf("x(7) < y(10): %d\n", x < y);
return 0;
}
x(7) > y(10): 0
x(7) < y(10): 1
==(等しい)と!=(等しくない)
==は等値比較、!=は不等比較です。
文字や整数だけでなく浮動小数点数にも使えますが、浮動小数点では丸め誤差に注意が必要です。
// == と != の基本、浮動小数点の注意点
#include <stdio.h>
#include <math.h>
int main(void) {
int p = 5, q = 5, r = 6;
printf("p == q: %d\n", p == q); // 1
printf("p != r: %d\n", p != r); // 1
double a = 0.1 + 0.2;
double b = 0.3;
printf("a == b (0.1+0.2 と 0.3 の比較): %d\n", a == b); // 多くの環境で 0
// 誤差を考慮した比較の例(許容誤差を設ける)
double eps = 1e-9;
printf("|a-b| < eps による近似比較: %d\n", fabs(a - b) < eps);
return 0;
}
p == q: 1
p != r: 1
a == b (0.1+0.2 と 0.3 の比較): 0
|a-b| < eps による近似比較: 1
文字列リテラル同士を==で比較するとポインタ比較になります。
文字列の内容を比較する場合はstrcmpを使うべきですが、これは別の記事で扱います。
= と == の違いに注意
=は代入、==は比較です。
ifの条件で代入してしまうミスは非常に多いです。
// 悪い例: ifの中で=を使ってしまう(代入になり、式全体の値が条件になる)
#include <stdio.h>
int main(void) {
int n = 0;
if (n = 0) { // ここは「nが0に代入され、その結果0が条件」となる -> 偽
printf("n = 0 の if: 真\n");
} else {
printf("n = 0 の if: 偽(elseが実行)\n");
}
if (n = 1) { // 「nが1に代入され、その結果1が条件」となる -> 真
printf("n = 1 の if: 真(ifが実行)\n");
} else {
printf("n = 1 の if: 偽\n");
}
return 0;
}
n = 0 の if: 偽(elseが実行)
n = 1 の if: 真(ifが実行)
// 良い例: == を使う、さらに()で意図を明確にする
#include <stdio.h>
int main(void) {
int n = 0;
if (n == 0) {
printf("n == 0: 真\n");
} else {
printf("n == 0: 偽\n");
}
return 0;
}
n == 0: 真
コンパイラの警告(-Wallなど)を有効にしておくと、代入の取り違えを早期に検出しやすくなります。
論理演算子(&&, ||, !)の使い方
&&(かつ)の基本
&&は両方の条件が真のとき真になります。
たとえば「20代か」を判定する場合は次のように書けます。
// && の基本: 範囲判定の例(20 <= age < 30 相当)
#include <stdio.h>
int main(void) {
int age = 25;
int is_twenty代 = (age > 19) && (age < 30); // >, < のみで書く
printf("age=%d は20代か: %d\n", age, is_twenty代);
return 0;
}
age=25 は20代か: 1
||(または)の基本
||はどちらか一方でも真なら真になります。
// || の基本: 複数学値のいずれかに該当
#include <stdio.h>
int main(void) {
char grade = 'B';
int is_pass = (grade == 'A') || (grade == 'B');
printf("grade=%c は合格ランク(AまたはB)か: %d\n", grade, is_pass);
return 0;
}
grade=B は合格ランク(AまたはB)か: 1
!(否定)の基本
!は真偽を反転します。
!0は1、!非0は0です。
// ! の基本
#include <stdio.h>
int main(void) {
printf("!0: %d\n", !0);
printf("!5: %d\n", !5);
int n = 0;
if (!n) {
printf("nは0なので!nは真\n");
}
return 0;
}
!0: 1
!5: 0
nは0なので!nは真
短絡評価(ショートサーキット)の基本
&&や||は必要のない側を評価しません。
これを短絡評価と呼びます。
&&は左が偽なら右を評価せずに偽、||は左が真なら右を評価せずに真になります。
// 短絡評価の観察: どちらの式が評価されたかを表示する
#include <stdio.h>
int log_and_return(const char *label, int value) {
printf("%s が評価されました(value=%d)\n", label, value);
return value;
}
int main(void) {
int a = 0, b = 1;
// 左が偽(0)なので右は評価されない
int r1 = log_and_return("左辺(AND)", a) && log_and_return("右辺(AND)", b);
printf("結果 a && b: %d\n", r1);
// 左が真(非0)なので右は評価されない
int r2 = log_and_return("左辺(OR)", b) || log_and_return("右辺(OR)", a);
printf("結果 b || a: %d\n", r2);
return 0;
}
左辺(AND) が評価されました(value=0)
結果 a && b: 0
左辺(OR) が評価されました(value=1)
結果 b || a: 1
副作用のある関数呼び出しや代入を条件式の右側に置くと、短絡評価のために実行されないことがあります。
条件式は「評価されなくても良い」設計にするのが安全です。
比較演算子との組み合わせ例
複合条件は括弧で区切ると読みやすくなります。
合格基準を「点数が70より大きく欠席が3未満、または満点」とする例です。
// 比較と論理の組み合わせ
#include <stdio.h>
int main(void) {
int score = 75;
int absence = 2;
int pass = ((score > 69) && (absence < 3)) || (score == 100);
printf("score=%d, absence=%d -> pass: %d\n", score, absence, pass);
return 0;
}
score=75, absence=2 -> pass: 1
if, whileでの活用
whileで配列を先頭から走査し、0が現れたら止める例です。
短絡評価により「範囲内か」を先に判定することで、安全にアクセスできます。
// whileで条件を組み合わせて安全に走査する
#include <stdio.h>
int main(void) {
int data[] = {5, 3, 0, 7, 9};
int n = (int)(sizeof(data) / sizeof(data[0]));
int i = 0;
// i < n が偽になった時点で右側は評価されない(短絡評価)
while ((i < n) && (data[i] != 0)) {
printf("data[%d] = %d\n", i, data[i]);
i++;
}
if ((i < n) && (data[i] == 0)) {
printf("最初の0をdata[%d]で検出\n", i);
}
return 0;
}
data[0] = 5
data[1] = 3
最初の0をdata[2]で検出
よくあるミスとコツ(初心者向け)
比較は論理より先に評価(優先順位)
C言語では、!、比較(<や==など)、&&、||の順に優先順位があります。
つまりa < b && b < cは(a < b) && (b < c)と解釈されます。
曖昧さを避けるため括弧で明示すると読みやすくなります。
以下は優先順位の目安です(高い→低い)。
| 優先度 | 演算子(一部) | 意味 |
|---|---|---|
| 高 | ! | 否定 |
| 中 | <, >, <=, >= | 大小比較 |
| 中 | ==, != | 等値比較 |
| 中 | && | 論理積 |
| 低 | || | 論理和 |
// 優先順位による違いの例
#include <stdio.h>
int main(void) {
int a = 0, b = 1, c = 2;
int r1 = a == b || c; // (a == b) || c と解釈 -> 0 || 2(真) -> 真(1)
int r2 = a == (b || c); // (b || c) は真(1) -> a == 1 -> 偽(0)
printf("r1: %d, r2: %d\n", r1, r2);
return 0;
}
r1: 1, r2: 0
意図が伝わる括弧づけは、バグの予防にも大きく役立ちます。
カッコ()で意図を明確に
演算子の優先順位に頼らず、論理ブロックは括弧でまとめると、将来の修正やレビュー時の誤解を避けられます。
// 括弧で論理ブロックを明示
#include <stdio.h>
int main(void) {
int x = 8;
int in_range = (x > 0) && (x < 10); // どちらの比較が && の左右なのか一目瞭然
printf("0 < x && x < 10: %d\n", in_range);
return 0;
}
0 < x && x < 10: 1
0/非0とtrue/falseの混同に注意
論理演算や比較の結果は0か1に正規化されますが、ifの条件としては「非0なら真」です。
したがってif (flag == 1)と書くと、flagが2などの非0でも偽になってしまいます。
// 0/非0と1の違い
#include <stdio.h>
int main(void) {
int flag = 2;
printf("flag を条件(非0)として評価: %d\n", flag != 0); // 1
printf("flag == 1 と等値比較: %d\n", flag == 1); // 0
// 論理演算の結果は常に0/1
printf("5 && 2: %d\n", 5 && 2); // 1
printf("5 || 0: %d\n", 5 || 0); // 1
return 0;
}
flag を条件(非0)として評価: 1
flag == 1 と等値比較: 0
5 && 2: 1
5 || 0: 1
「真かどうか」を見たいときはif (flag)のように書くのがC言語の慣習です。
true/falseを使うならstdbool.h
C99以降ではstdbool.hでbool、true、falseが使えます。
内部的には0/1の整数なので、printfでは%dで出力できます。
// stdbool.h の利用
#include <stdio.h>
#include <stdbool.h>
int main(void) {
bool ok = true;
bool ng = false;
printf("ok=%d, ng=%d\n", ok, ng);
int n = 2;
bool b = n; // 非0を代入するとtrue(1)になる
printf("n=2 を bool に代入 -> b=%d\n", b);
if (!ng && ok) {
printf("!ng && ok は真です\n");
}
return 0;
}
ok=1, ng=0
n=2 を bool に代入 -> b=1
!ng && ok は真です
参考: 真理値表(0/1で表記)
論理演算子の基本的な真理値は次の通りです。
入力は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 |
実際のコードでは「非0は真」ですが、論理演算の結果は0か1に正規化されます。
まとめ
比較演算子と論理演算子は、条件分岐やループの表現力を支える基本要素です。
Cでは0が偽、非0が真であり、比較や論理演算の結果は整数の0/1になります。
= と == の取り違え、短絡評価による未評価、優先順位の誤解は初学者がつまずきやすいポイントです。
常に括弧で意図を明確にし、-Wallなどの警告を活用し、必要に応じてstdbool.hでboolを使うことで、読みやすく安全なコードになります。
今回学んだ>、<、==、!=、&&、||、!を使いこなし、条件表現を着実に身につけていきましょう。
