C言語の三項演算子は、短い条件分岐を1行で書ける便利な演算子です。
しかし、むやみに使うとコードの意味が分かりにくくなることもあります。
本記事では、C言語の三項演算子の基本構文から、if文との違い・可読性・注意点までを、サンプルコードと図解を交えて詳しく解説します。
三項演算子とは何か
三項演算子の基本構文
三項演算子は、”条件演算子”とも呼ばれる演算子で、「条件 ? 真の場合の値 : 偽の場合の値」という形で書きます。

三項演算子の基本構文は、次のようになります。
条件式 ? 式1 : 式2
C言語では、条件式の結果が「真」か「偽」かによって、評価される式が切り替わります。
- 条件式が真(0以外)のとき →
式1が評価され、その値が結果になる - 条件式が偽(0)のとき →
式2が評価され、その値が結果になる
たとえば、次のような式が典型的です。
int x = 5;
int sign = (x >= 0) ? 1 : -1;
この場合、x >= 0が真なので、signには1が代入されます。
三項演算子でできることと特徴
三項演算子は、主に「条件によって値を切り替える」用途で使われます。
if文と同じように条件分岐ができますが、次のような特徴があります。
1つ目の特徴は、式として値を返すことです。
if文は文(statement)ですが、三項演算子は「演算子」なので、関数の引数に書いたり、他の式の一部として使うことができます。
2つ目の特徴は、簡潔に1行で書けることです。
特に、短い条件分岐で代入するだけのような処理では、コード量を減らし、処理内容をコンパクトに表現できます。
一方で、三項演算子には可読性を損ないやすいという側面もあります。
条件式や真偽の場合の式が複雑になりすぎると、一目で意味を理解するのが難しくなってしまいます。
この点は後半で詳しく解説します。
if文との共通点と違いの概要
三項演算子とif文は、どちらも「条件によって処理を変える」ための構文です。
まずは、ざっくりとした共通点と違いを整理します。

文章で整理すると、次のような違いがあります。
- 共通点
条件式があり、その結果に応じて処理や値を切り替える点は同じです。 - 違い
if文は文(statement)であり、複数の文をまとめて実行できます。対して三項演算子は式(expression)で、結果として1つの値を返すことが前提です。
この違いを理解しておくと、「どこで三項演算子を使うべきか」「どこからif文に切り替えるべきか」の判断がしやすくなります。
C言語の三項演算子の使い方
真偽で値を切り替える基本的な書き方
三項演算子の一番シンプルな使い方は、条件によって2つの値のどちらかを選ぶことです。

次のサンプルコードで、値が正か負かを見て文字列を切り替える例を示します。
#include <stdio.h>
int main(void) {
int x = -3;
// xが0以上なら"non-negative"、それ以外(負)なら"negative"という文字列を選ぶ
const char *result = (x >= 0) ? "non-negative" : "negative";
printf("x = %d は %s です\n", x, result);
return 0;
}
x = -3 は negative です
ここでは、(x >= 0) ? "non-negative" : "negative"という式が、条件に応じた文字列を返しています。
このように、「どちらか一方の値を選ぶ」用途に最もよく使われます。
変数への代入に三項演算子を使う方法
代入と三項演算子は、とても相性が良いです。
条件に応じて代入する値が変わる場合、if文で書くとやや冗長になりがちですが、三項演算子を使うと1行で書けます。
以下の例では、整数の絶対値を計算しています。

#include <stdio.h>
int main(void) {
int x = -10;
// if文で書くとこのようなイメージ
int abs1;
if (x >= 0) {
abs1 = x;
} else {
abs1 = -x;
}
// 三項演算子で同じ処理を1行で書く
int abs2 = (x >= 0) ? x : -x;
printf("x = %d\n", x);
printf("abs1(if文) = %d\n", abs1);
printf("abs2(三項演算子) = %d\n", abs2);
return 0;
}
x = -10
abs1(if文) = 10
abs2(三項演算子) = 10
このように、代入する値だけが条件で変わる場合には、三項演算子を使うと読みやすくなることが多いです。
printfと三項演算子を組み合わせる書き方
三項演算子は「式」なので、そのままprintfの引数に書くこともできます。
これにより、メッセージ文言を条件によって切り替えるような用途でコードを短くできます。

次の例では、点数によって「合格」「不合格」を表示します。
#include <stdio.h>
int main(void) {
int score = 75;
// scoreが60以上なら"合格"、それ以外なら"不合格"を表示する
printf("点数: %d 点 → %s\n",
score,
(score >= 60) ? "合格" : "不合格");
return 0;
}
点数: 75 点 → 合格
ここでは、(score >= 60) ? "合格" : "不合格"が、そのまま%sに渡される文字列として機能しています。
「出力フォーマットは固定で、文字列の一部だけ条件で変えたい」ときに、非常に便利な書き方です。
入力値による分岐処理の簡潔な記述例
ユーザーからの入力値に応じて結果を分ける場合も、簡単なものであれば三項演算子が活躍します。

以下は、年齢を入力して成人かどうかを判定する例です。
#include <stdio.h>
int main(void) {
int age;
printf("年齢を入力してください: ");
scanf("%d", &age);
// 入力された年齢によって、「成人」か「未成年」かを切り替えて表示
printf("あなたは%sです。\n",
(age >= 20) ? "成人" : "未成年");
return 0;
}
年齢を入力してください: 18
あなたは未成年です。
このように、単純な条件分岐で出力メッセージだけ変えたい場合には、三項演算子を使うことでコードをかなりスッキリさせられます。
if文との違いと使い分け
if文と三項演算子の構文上の違い
if文と三項演算子は見た目も役割も似ていますが、構文の性質が根本的に違うことを理解しておくことが大切です。

if文の基本形と、同等の三項演算子を比較してみます。
#include <stdio.h>
int main(void) {
int x = -3;
int sign_if;
int sign_ternary;
// if文での書き方
if (x >= 0) {
sign_if = 1;
} else {
sign_if = -1;
}
// 三項演算子での書き方
sign_ternary = (x >= 0) ? 1 : -1;
printf("sign_if = %d\n", sign_if);
printf("sign_ternary = %d\n", sign_ternary);
return 0;
}
sign_if = 1
sign_ternary = 1
結果は同じですが、次のような違いがあります。
- if文
文(statement)として、ブロック{ }の中に複数の処理を書けます。代入だけでなく、関数呼び出しやループ開始など、何でも記述できます。 - 三項演算子
式(expression)として、「1つの値」を選んで返すことが前提です。そのため、主な使い方は「代入の右辺」「関数の引数」「return文の右側」などになります。
この違いから、複雑な処理を三項演算子で書こうとするのは不向きであることが分かります。
三項演算子でできない処理
三項演算子はあくまで「値を返す式」なので、if文で普通にできることの一部は、そのままでは書けません。
代表的な制約は次の通りです。
- 完全に異なる複数の処理を、それぞれの分岐で実行したい場合
- ブロック内に複数行の処理を書きたい場合
- それぞれの分岐で別々の変数を宣言したい場合
例として、ログ出力と計算を同時に行う処理を、三項演算子で無理に書こうとするとどうなるかを見てみます。
#include <stdio.h>
int main(void) {
int x = 5;
int y;
// 正しいif文の使い方
if (x >= 0) {
printf("xは非負の値です\n");
y = x;
} else {
printf("xは負の値です\n");
y = -x;
}
printf("y = %d\n", y);
return 0;
}
xは非負の値です
y = 5
この処理を、三項演算子だけで完全に置き換えるのは実質的に不可能です。
三項演算子の中でもprintfを呼び出すことはできますが、コードが読みにくく、バグの原因にもなります。
「副作用を伴う処理(画面出力やファイル書き込みなど)を複数組み合わせる」ような場合は、素直にif文を使うべきです。
ネストした条件分岐と三項演算子
三項演算子は入れ子(ネスト)にして使うこともできますが、可読性を大きく損ないやすいため、一般的には避けるべきです。

次の例は、点数によって評価を「優」「良」「可」「不可」と4段階に分けるプログラムです。
まず、ネストした三項演算子で無理に書いた例を示します。
#include <stdio.h>
int main(void) {
int score = 78;
// ネストした三項演算子の悪い例
const char *grade =
(score >= 80) ? "優" :
(score >= 70) ? "良" :
(score >= 60) ? "可" : "不可";
printf("点数: %d 点 → 評価: %s\n", score, grade);
return 0;
}
点数: 78 点 → 評価: 良
動作としては正しくても、条件が増えるほど見通しが悪くなっていきます。
同じ処理をif文で書くと、次のようになります。
#include <stdio.h>
int main(void) {
int score = 78;
const char *grade;
if (score >= 80) {
grade = "優";
} else if (score >= 70) {
grade = "良";
} else if (score >= 60) {
grade = "可";
} else {
grade = "不可";
}
printf("点数: %d 点 → 評価: %s\n", score, grade);
return 0;
}
点数: 78 点 → 評価: 良
このように、条件の数が多い・分岐の階層が深い場合には、if–else if–else構造の方が分かりやすく、安全です。
三項演算子とif文のパフォーマンス比較
三項演算子の話題になると、「if文より速いのか」という疑問を持つ方も多いです。
結論から言うと、現代的なコンパイラでは、ほとんどの場合パフォーマンス差は気にしなくてよいです。

コンパイラの最適化が有効な環境では、if文も三項演算子も、ほぼ同じ機械語命令に変換されることが多いです。
そのため、パフォーマンスではなく、可読性・保守性を優先して書き方を選ぶのが実務的です。
ただし、組み込み開発などの非常にシビアな環境では、コンパイラの挙動を確認した上で適切な書き方を選ぶこともあります。
その場合も、まずは「読みやすいコードを書き、必要があればプロファイルを取って最適化する」のが基本方針です。
三項演算子の可読性と注意点
三項演算子の可読性が下がる書き方の例
三項演算子は便利な一方で、書き方によっては一気に読みにくくなることがあります。
特に避けたいのは、条件式が長すぎる、真偽の式が複雑すぎるといったケースです。

次のコードは、悪い例です。
#include <stdio.h>
int main(void) {
int a = 10, b = 20, c = 5;
int max;
// 可読性の低い三項演算子の例(あえて1行に詰め込んでいる)
max = (a > b && a > c) ? a + 100 : (b > c ? b * 2 : c - 3);
printf("max = %d\n", max);
return 0;
}
max = 120
処理の内容を理解するのに、何度も式を読み返す必要があるはずです。
「ロジックを理解するのに時間がかかる三項演算子」は、たとえ行数が減っても良いコードとは言えません。
こうした場合は、if文に書き換える、あるいは途中計算用の変数を導入するなどして、読みやすさを優先しましょう。
ネストした三項演算子を避けるべき理由
前述の通り、三項演算子はネストして使うことができますが、バグの温床になりやすいため、基本的には避けるべきです。

ネストした三項演算子が危険な理由をまとめると、次のようになります。
- 条件と結果の対応関係が一目で分かりにくい
- カッコの付け忘れや誤ったグルーピングによるバグが起こりやすい
- 後から条件を追加・変更する際にミスを誘発しやすい
そのため、三項演算子のネストは「原則禁止」としているコーディング規約も少なくありません。
複数段階の分岐が必要であれば、素直にif–else if–elseやswitch文を使うことが推奨されます。
三項演算子を使うのに適したケース
ここまで見ると「三項演算子は危険なのでは」と感じるかもしれませんが、条件が単純で、値を1つ選ぶだけというケースでは、非常に有用です。

具体的には、次のようなケースが向いています。
- 「AかBか」の2択で値を決める代入
例: 最大値・最小値・符号・境界チェックなど - 出力メッセージの一部を変える
例: 「ON/OFF」「YES/NO」「成功/失敗」といったクリアな2択 - 関数の戻り値を条件で切り替える
例: パラメータチェックに失敗したらデフォルト値を返す、など
シンプルな例を1つ示します。
#include <stdio.h>
int abs_value(int x) {
// 戻り値に三項演算子を使う典型例
return (x >= 0) ? x : -x;
}
int main(void) {
int x = -7;
printf("abs_value(%d) = %d\n", x, abs_value(x));
return 0;
}
abs_value(-7) = 7
このような短く、意味が明確な条件分岐では、三項演算子はコードを分かりやすくしてくれます。
コーディング規約における三項演算子の扱い
多くの現場では、プロジェクトごとにコーディング規約を定めており、その中で三項演算子の扱いについてもルールが設けられていることがあります。
代表的な方針として、次のようなものがあります。
- 「単純な代入・戻り値に限定して使用可」
2択で値を決めるだけの用途に限定し、それ以外では使用しない方針です。 - 「ネストは禁止」
三項演算子の入れ子は原則禁止とし、複雑な場合はif文を使う方針です。 - 「可読性を優先し、迷ったらif文」
「一読して意味が分からない三項演算子は禁止」という形で、可読性を重視するルールにしている場合もあります。

自分で個人開発を行う場合でも、「自分なりの三項演算子利用ルール」を決めておくと、コードのスタイルが安定し、保守しやすくなります。
まとめ
三項演算子は、「条件に応じて1つの値を選ぶ」場面でif文よりも簡潔に書ける強力な道具です。
一方で、条件や結果の式が複雑になったり、ネストを多用したりすると、途端に可読性が下がり、バグの温床になりかねません。
シンプルな2択で代入・printf・returnに使うといったルールを決め、迷ったらif文に戻すことで、三項演算子のメリットだけを上手に活かすことができます。
