C言語基礎|論理否定と条件の書き換え

はじめに:条件は「同じ意味でも、読みやすさが変わる」

C言語でプログラムを書いていると、同じ判定なのに

  • ある書き方はスッと読める
  • 別の書き方は一瞬考え込む

…みたいなこと、よく起きます。

とくに 入力チェックループの継続条件 は、条件式が長くなりがち。
そこで役に立つのが 論理否定演算子 !条件の書き換え(ド・モルガンの法則) です。

ここでは、

  • ! が何をしているのか
  • 継続条件と終了条件の関係
  • ド・モルガンの法則で「同じ意味に変換できる」こと

を、表と図でやさしく整理していきます。

サンプル:入力値が 10〜99 でなければ再入力

「2桁の整数(10〜99)だけ受け付ける入力チェック」プログラムを例に解説します。

プロジェクト名:chap4-3-1 ソースファイル名:chap4-3-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

// 2桁の整数(10〜99)のみ受け付け、OKなら結果を表示
#include <stdio.h>

int main(void)
{
    int code;

    do {
        printf("Enter a 2-digit code (10 to 99): ");
        scanf("%d", &code);
    } while (code < 10 || code > 99);   // 継続条件:不当なら繰り返す

    printf("Accepted. Your code is %d.\n", code);
    return 0;
}

実行例

Enter a 2-digit code (10 to 99): 7
Enter a 2-digit code (10 to 99): 120
Enter a 2-digit code (10 to 99): 42
Accepted. Your code is 42.

まずは復習:do文の継続条件は「繰り返す条件」

このサンプルの do 文はこうです。

do {
    ...
} while (code < 10 || code > 99);

ここでの while (式) の式は 制御式
その意味は 式が真(非0)なら繰り返す です。

つまり今回の制御式は、

  • code が 10 未満、または 99 より大きい
    範囲外(不当)なら繰り返す

という 継続条件 になっています。

論理否定演算子 ! は何をする演算子?

!(論理否定)は、ざっくり言うと

  • 真っぽいものを偽に
  • 偽っぽいものを真に

ひっくり返す演算子です。

表:論理否定演算子の意味

形式意味結果の型
!aa が 0 なら 1、そうでなければ 0int

C言語の条件式では、慣習として

  • 0 が偽
  • 0以外が真

です。だから ! は「0かどうか」を判定して反転させている、と思うと理解が早いです。

例:値の反転イメージ

a!a
01
50
-30

「不当なら続ける」を「妥当でないなら続ける」に書き換える

元の制御式(継続条件)はこれでした。

code < 10 || code > 99

これは「code が範囲外か?」という判定です。
一方で「code が範囲内か?」という判定はこう書けます。

code >= 10 && code <= 99

そして「範囲内ではないか?」(妥当ではないか?)は、これの否定なのでこうなります。

!(code >= 10 && code <= 99)

つまり、次の2つは 同じ意味 になります。

表:同じ意味になる2つの条件式

目的条件式日本語のニュアンス
継続条件(不当なら繰り返す)code < 10 || code > 99範囲外ならやり直し
終了条件の否定(妥当でなければ繰り返す)!(code >= 10 && code <= 99)範囲内でないならやり直し

どっちが良いかは「好み」でもありますが、文脈で読みやすいほうを選べるのが強みです。

継続条件と終了条件はコインの裏表

同じ意味を、見え方を変えて表現するとこうなります。

① 継続条件(不当なら続ける)
   code < 10 || code > 99

② 終了条件(妥当なら終わる)の否定
   !(code >= 10 && code <= 99)
  • ① は「続ける理由」を書いている。
  • ② は「終わる理由」の反対を書いている。

中身は同じでも、読み手が理解しやすい方向が変わるんですね。

ド・モルガンの法則:否定を中に入れると && と || が入れ替わる

ここが本題のキモです。

ド・モルガン(C言語風)

元の式同じ意味の式
x && y!( !x || !y )
x || y!( !x && !y )

つまり、否定を配る(内側に押し込む)ときは

  • && と || が入れ替わる。
  • 各条件が否定される。

というルールです。

実際に変形してみる(今回の条件で確認)

「妥当」はこうでした。

(code >= 10 && code <= 99)

これを否定したものが「妥当でない」。

!(code >= 10 && code <= 99)

ここでド・モルガンを使うと、

  • (A && B) の否定は (!A || !B)

なので、

  • !(code >= 10) || !(code <= 99)

さらに比較の否定は次のように書けます。

  • !(code >= 10) は code < 10
  • !(code <= 99) は code > 99

だから最終的に、

code < 10 || code > 99

に戻ります。ちゃんと一致しましたね。

表:変形のステップ

ステップ
妥当code >= 10 && code <= 99
妥当でない(否定)!(code >= 10 && code <= 99)
ド・モルガン適用!(code >= 10) || !(code <= 99)
比較の否定を整理code < 10 || code > 99

どっちの書き方が読みやすい?(実務の感覚)

わりと実務ではこういう基準で選ぶことが多いです。

こういう時読みやすい傾向
「範囲外ならやり直し」みたいに例外側を強調したいcode < 10 || code > 99
「範囲内」というルールを強調したい!(code >= 10 && code <= 99) ではなく、むしろ code >= 10 && code <= 99 を終了条件側に回す発想が合う
条件が複雑で否定が入り乱れるド・モルガンで否定を整理して、否定記号 ! を減らす

特に、!( ... ) が何重にもなると急に読みづらくなるので、
「否定を配ってスッキリさせる」ド・モルガンはかなり頼れます。

ここで登場した命令の書式まとめ

do-while 文

形式役割
do { ... } while (式);まず本体を実行してから式を評価。式が真なら繰り返す。

論理否定

形式役割
!aa が 0 なら 1、それ以外なら 0(真偽を反転)

論理積と論理和

演算子意味
x && y両方が真なら真
x || yどちらかが真なら真

まとめ:条件の書き換えは「読みやすさの武器」

  • ! は真偽を反転する。
  • 継続条件と終了条件は、見方を変えただけで同じ意味になりうる。
  • ド・モルガンの法則で、否定を整理して読みやすい条件式にできる。

条件式は「正しい」だけじゃなく、「読みやすい」も超大事。
この章を押さえると、入力チェックや複雑な分岐がグッと書きやすくなります。