短い分岐なら条件演算子(三項演算子)を使うとif文を1行にまとめられます。
C言語では条件 ? 真の式 : 偽の式
という書き方で、式として値を返すのが特徴です。
この記事では、基本の構文からif文の書き換え、実用例、注意点まで、初心者向けに丁寧に解説します。
条件演算子(三項演算子)の基本
条件演算子とは?if文を1行にする仕組み
条件演算子はif
とelse
で書く単純な二択の分岐を1行の式として表現できます。
次の2つは意味が同じです。
// if-else での分岐
int x;
if (a > b) {
x = a;
} else {
x = b;
}
// 条件演算子での分岐 (同じ意味)
int y = (a > b) ? a : b;
ポイントは「式である」ことです。
代入やreturn
の右辺に直接書けるため、短い分岐にはとても相性が良いです。
構文: 条件 ? 真の式 : 偽の式
書式は次の通りです。
条件 ? 真の式 : 偽の式
条件が真(非0)なら真の式
が評価され、その値が結果になります。
偽(0)なら偽の式
が評価されます。
なお、真の式
と偽の式
はどちらか一方しか評価されません。
次の表で各部の役割を整理します。
部分 | 役割 | 補足 |
---|---|---|
条件 | 判定したい式 | 0なら偽、0以外なら真 |
真の式 | 条件が真のときの値(式) | 評価されるのは真のときのみ |
偽の式 | 条件が偽のときの値(式) | 評価されるのは偽のときのみ |
値を返す式として使える
条件演算子は「値を返す式」なので、代入や関数の引数、return
の右辺にそのまま書けます。
型は両方の式の型が両立するように決まります(整数同士、浮動小数同士、互換のポインタ同士、など)。
// 代入
int max = (a > b) ? a : b;
// 引数の中で
printf("%s\n", (n % 2 == 0) ? "even" : "odd");
// return の中で
return (score >= 60) ? PASS : FAIL;
両側の式に副作用(インクリメントやファイル書き込みなど)を詰め込むのは避けましょう。
読みづらく、デバッグも難しくなります。
1行のメリットと向き不向き
短い分岐を1行で書けるのが最大の利点です。
コードがコンパクトになり、視線の移動も少なくなります。
一方で、長い処理や多段の条件には向きません。
可読性が下がるため、その場合は素直にif
に戻すのがよいです。
次のように考えると判断しやすくなります。
使うべき場面 | 避けるべき場面 |
---|---|
値を選んで代入・返却・表示するだけ | 複数行にわたる処理、複数の副作用 |
短いメッセージ切替や数値の二択 | 条件が複雑(複数の論理演算を含む) |
1回の選択で済む場面 | ネストして三択以上にしたくなる場面 |
if文から三項演算子への書き換え
if-elseの基本を三項演算子で表現
基本形は次の対応になります。
右辺に結果が現れるのが肝心です。
// if-else 版
int result;
if (ok) {
result = 1;
} else {
result = 0;
}
// 三項演算子版
int result2 = ok ? 1 : 0;
置き換えの考え方
- 「変数 = …」の形にできる分岐は置き換えやすいです。
- 「実行する処理が2種類ある」だけのときは、無理に置き換えない方が読みやすいことが多いです。
代入の省略: a = 条件 ? X : Y
よくある書き方はa = (条件) ? X : Y;
です。
二者択一で値を選ぶ場面に最適です。
// a と b のうち小さい方を min に代入
int min = (a < b) ? a : b;
returnで使う書き方
関数から返す値を切り替えるときにも簡潔です。
早期returnで分岐を畳めるので、ネストが浅くなります。
// 偶奇で文字列を返す
const char* even_or_odd(int n) {
return (n % 2 == 0) ? "偶数" : "奇数";
}
短い条件だけに使うのがコツ
三項演算子のネストは読みづらさの元です。
例えばn > 0 ? "正" : n == 0 ? "ゼロ" : "負"
のような書き方は、if-else if-elseに展開した方が明確です。
使い方の例
正負の判定を1行で
0以上かどうかをメッセージで切り替えます。
#include <stdio.h>
int main(void) {
int xs[] = {-5, 0, 7};
int n = (int)(sizeof(xs) / sizeof(xs[0]));
for (int i = 0; i < n; i++) {
int x = xs[i];
// 条件が真なら「非負(0以上)」、偽なら「負」
printf("%d は%sです\n", x, (x >= 0) ? "非負(0以上)" : "負");
}
return 0;
}
-5 は負です
0 は非負(0以上)です
7 は非負(0以上)です
合格判定のメッセージ切り替え
点数score
が60以上なら合格、そうでなければ不合格と表示します。
#include <stdio.h>
int main(void) {
int scores[] = {30, 60, 85};
int n = (int)(sizeof(scores) / sizeof(scores[0]));
for (int i = 0; i < n; i++) {
int score = scores[i];
const char* msg = (score >= 60) ? "合格" : "不合格";
printf("%3d 点: %s\n", score, msg);
}
return 0;
}
30 点: 不合格
60 点: 合格
85 点: 合格
最小値(小さい方)を選ぶ
二つの整数a
とb
の小さい方を選びます。
#include <stdio.h>
int main(void) {
int a = 10;
int b = 4;
int min = (a < b) ? a : b; // 真なら a、偽なら b
printf("min(%d, %d) = %d\n", a, b, min);
return 0;
}
min(10, 4) = 4
真偽フラグで文字を選ぶ
bool
のフラグからシンプルに1文字を選びます。
#include <stdio.h>
#include <stdbool.h> // bool, true, false を使う
int main(void) {
bool enabled = true;
char mark = enabled ? 'Y' : 'N'; // 有効なら 'Y'、無効なら 'N'
printf("enabled=%s => mark=%c\n", enabled ? "true" : "false", mark);
enabled = false;
mark = enabled ? 'Y' : 'N';
printf("enabled=%s => mark=%c\n", enabled ? "true" : "false", mark);
return 0;
}
enabled=true => mark=Y
enabled=false => mark=N
printfで出力を切り替える
1回のprintfで文字列だけを切り替えるのが読みやすいです。
#include <stdio.h>
int main(void) {
int n1 = 3;
int n2 = 8;
// おすすめ: 書式は固定、%s の中身だけを切り替える
printf("n1=%d は %s です\n", n1, (n1 % 2 == 0) ? "偶数" : "奇数");
// こう書くことも可能だが、呼び出しが2つに分かれて読みづらい
(n2 % 2 == 0) ? puts("n2 は偶数です") : puts("n2 は奇数です");
return 0;
}
n1=3 は 奇数 です
n2 は偶数です
注意点と読みやすい書き方
ネスト(入れ子)は避ける
三択以上を三項演算子でつなぐと一気に読みにくくなります。
次のような書き方は避けましょう。
// 読みにくい例: 三項演算子のネスト
// const char* label = (n > 0) ? "正" : (n == 0) ? "ゼロ" : "負";
// 代わりに if-else if-else で明確に
const char* label_of(int n) {
if (n > 0) {
return "正";
} else if (n == 0) {
return "ゼロ";
} else {
return "負";
}
}
長い処理はif文を使う
分岐ごとに複数行の処理がある場合、三項演算子に押し込めると可読性を損ないます。
ログ出力→計算→代入
のような流れは素直にif-else
へ。
条件演算子は「値を選ぶ」場面専用と考えるのが安全です。
条件と結果は短くシンプルに
条件式は1つか2つの比較にとどめ、結果も定数・変数・短い式
に留めると読みやすくなります。
冗長になりそうなら、途中の結果を一旦変数に入れてから使いましょう。
// 悪い例: 長い計算をそのまま詰め込む
// price = is_vip ? base * (1.0 - (coupon_rate * seasonal_rate)) : base;
// 良い例: 中間値を分けて見通しを良くする
double discount = coupon_rate * seasonal_rate;
price = is_vip ? base * (1.0 - discount) : base;
括弧で意図をはっきり書く
括弧で区切ると誤読を防げます。
特に結果側が演算式のときは括弧で包むのが無難です。
// 括弧で意図を明確に
int result = (cond) ? (x + y) : (x - y);
演算子の優先順位に注意
条件演算子?:
は多くの演算子より優先順位が低い一方で、=
(代入)よりは高いです。
また?:
は右結合(右から結びつく)です。
迷ったら必ず括弧で明示しましょう。
以下の小さなプログラムは、同じ見た目でも括弧がないと全く違う意味になる例です。
#include <stdio.h>
int main(void) {
int a = 1, b = 2;
// 優先順位: + が ?: より高いので、(a + b) ? 10 : 20 と解釈される
int r1 = a + b ? 10 : 20; // (1 + 2) は真 → 10
// 括弧で ?: を先に評価させる
int r2 = a + (b ? 10 : 20); // b は 2 で真 → 10、よって 1 + 10 = 11
printf("r1=%d, r2=%d\n", r1, r2);
// ?: は右結合。a ? b : c ? 100 : 200 は a ? b : (c ? 100 : 200)
int a0 = 0, c = 2;
int x = a0 ? b : c ? 100 : 200; // a0 が偽 → (c ? 100 : 200) → 100
printf("x=%d\n", x);
// 代入より ?: の方が強く結びつく
int m, n = 5;
m = (n % 2 == 0) ? 100 : 200; // 実際の解釈は m = ((n % 2 == 0) ? 100 : 200)
printf("m=%d\n", m);
return 0;
}
r1=10, r2=11
x=100
m=200
評価順序にも注意が必要です。
条件式が先に評価され、真か偽かでどちらか一方の式だけが評価されます。
したがって、次のような副作用を両側に書くと読みにくく、バグの温床です。
// 推奨しない: 両側に副作用がある例 (片方しか呼ばれないが読み手を惑わせる)
(cond) ? do_side_effect_A() : do_side_effect_B();
副作用を伴う処理はif-else
に戻しましょう。
まとめ
条件演算子?:
は「値の二択」を1行で書ける強力な道具です。
構文は条件 ? 真の式 : 偽の式
で、代入・関数引数・return
の右辺として活躍します。
読みやすさを保つコツは、短い条件・短い結果だけに使うこと、ネストや副作用は避けること、そして迷ったら括弧で明示することです。
向き不向きを見極めて、if文と三項演算子を使い分けできるようになれば、コードはより簡潔で意図が伝わりやすくなります。