論理値 | Programming Place Plus 新C++編

トップページ新C++編

このページの概要 🔗

このページでは、論理値について取り上げます。for文を使ったときに条件式を記述しましたが、条件式は実行されて値になっています。条件式から得られる値は論理値です。ここではまず、論理値とはどういう存在なのかを理解し、for文の条件式をもっと自由に指定できるようにする知識を得ることにします。

このページの解説は C++14 をベースとしています

以下は目次です。要点だけをさっと確認したい方は、「まとめ」をご覧ください。



論理値 🔗

for文」のページで、for文を使って、同じコードを何度も繰り返し実行させられるようになりました。まだ「一定回数だけ」繰り返すという書き方にとどめていますが、for文の条件式はもっと柔軟な指定ができます。そこで、このページではまず、条件式というものをもう少し掘り下げて理解していこうと思います。

条件式を柔軟に設定できるようになると、たとえば電卓プログラムを、「“exit” と入力されるまで繰り返す」ように改造できます。

ここまでの電卓プログラムは、「整数 演算子 整数」というかたちの入力しか受け付けないので、“exit” のように文字列を1つだけ入力されたときの判定にはまだワンステップ必要です。

条件式を理解するにあたって、まず論理値を理解しておきましょう。論理値 (logical value) とは、ある条件が正しいか正しくないかという2択を表現する値のことです。「正しい」ことを「(しん) (true) 」といい、正しくないことを「(ぎ)(false)」といいます。

bool型、true、false 🔗

論理値を表現する型として、bool型 (bool type) があります。いつものように、変数を宣言するときの型に使えます。

bool 変数名 {初期値};
bool 変数名 {};
bool 変数名 = 初期値;

bool型は論理値を表現するのですから、bool型の変数に入れておける値は、真か偽かの2択です。C++ では、真を true、偽を false で表現します。true と false は、論理値リテラル (boolean literal) と呼ばれるリテラルの一種です。

なお、初期値の指定を省略して {} とだけ記述した場合は、false で初期化されます。

また、型の分類上、bool型は整数型の一種ということになっています。あとで取り上げますが、int型の値とのあいだで相互に変換することが可能です。

【C言語プログラマー】C言語でも C99 から、stdbool.h をインクルードすることで bool、true、false を使用できますが、それぞれマクロであって、C++ に用意されているものとは別物です。C++ でプログラムを書くのなら、C++ の標準のものだけを使うようにしましょう。

bool型の変数を宣言するコードを次のように書けます。

bool is_completed {true};
bool is_error {false};

bool型の変数には、それが論理値であることが分かりやすいような名前をつける習慣をもつ人が多いです。よくあるのは、先頭を「is」「has」「can」といった単語で始める名前です。is_completed は完了しているかどうかを、is_error はエラーが起きているかどうかを true/false の2択で表現する変数であることが分かります。

bool型の出力 🔗

bool型の値も std::cout を使って出力できますが、その結果は予想に反するものです。

#include <iostream>

int main()
{
    std::cout << true << "\n";
    std::cout << false << "\n";

    bool b {true};
    std::cout << b << "\n";
    b = false;
    std::cout << b << "\n";
}

実行結果:

1
0
1
0

true を出力すると 1 になり、false を出力すると 0 になります。


bool型の値を、文字列での表現で出力してほしいときは、次のように、std::boolalpha という記述を挟み込みます。

#include <iostream>

int main()
{
    std::cout << std::boolalpha << true << "\n";
    std::cout << std::boolalpha << false << "\n";

    bool b {true};
    std::cout << std::boolalpha << b << "\n";
    b = false;
    std::cout << std::boolalpha << b << "\n";
}

実行結果:

true
false
true
false

このように、std::cout を使うときに、<< std::boolalpha を挟み込むと、これより後ろ側で指定した bool型の値を、文字列の表現で出力するようになります

【上級】<< std::noboolalpha を挟むと、0 と 1 の出力に戻ります。たとえば、std::cout << std::boolalpha << true << std::noboolalpha << false << "\n"; とすると、true と 0 が出力されます。

【上級】“true” や “false” といわず「文字列の表現」といっているのは、表現がロケール(実行環境の言語などの設定)によって異なる可能性があるからです。日本語や英語では “true”、“false” で問題ありません。

bool型の入力 🔗

bool型の入力でも std::cin を使えますが、出力のときと同様、普通に使うと、0 か 1 かで入力しなければなりません。

#include <iostream>

int main()
{
    std::cout << "Please enter the integer.\n";
    bool b {};
    std::cin >> b;
    std::cout << std::boolalpha << b << "\n";
}

実行結果:

Please enter the integer.
1  <-- 入力した内容
true

実行結果:

Please enter the integer.
0  <-- 入力した内容
false

入力された整数が 0 なら false に、1 なら true になります。

【上級】0 でも 1 でもない整数を入力した場合は true が得られますが、同時にエラーも発生しています。std::cin がエラーを抱えた状態になるので(failbit が立つ)、その後の挙動に注意が必要です。[3]

bool型の値を文字列表現で入力したものを受け取りたいときは、出力のときと同じで、std::boolalpha を挟み込む必要があります。

#include <iostream>

int main()
{
    std::cout << "Please enter the boolean.\n";
    bool b {};
    std::cin >> std::boolalpha >> b;
    std::cout << std::boolalpha << b << "\n";
}

実行結果:

Please enter the boolean.
true  <-- 入力した内容
true

実行結果:

Please enter the boolean.
false  <-- 入力した内容
false

true という入力で true が出力され、false という入力で false が出力されました。どちらでもない入力はすべて false になります[4]。たとえば、TRUE のように大文字で入力された場合、それは true ではないので、false になります。

【上級】出力のところのコラムでも触れたとおり、true や false に対応する文字列表現は、ロケールによって異なる可能性があります。日本語や英語の環境では、“true” や “false” で問題ありません。

整数と bool型の値の変換

int型などの整数と bool型の値は相互に変換できます。

bool型の値を整数に変換する場合、true は 1 に、false は 0 に置き換わります。[1]

#include <iostream>

int main()
{
    bool b {true};
    int x {b};      // true -> 1
    std::cout << x << "\n";

    b = false;
    x = b;          // false -> 0
    std::cout << x << "\n";
}

実行結果:

1
0

一方、整数値を bool型の値に変換する場合、0 は false に、0以外は true に置き換わります。[2]ただしこちらは、コンパイル時に警告やエラーを出す場合があります。

#include <iostream>

int main()
{
    int value {0};
    bool b = value;  // 0 -> false
    std::cout << std::boolalpha << b << "\n";
    
    value = 1;
    b = value;       // 1 -> true
    std::cout << std::boolalpha << b << "\n";

    value = 2;
    b = value;       // 2 -> true
    std::cout << std::boolalpha << b << "\n";
}

実行結果:

false
true
true

変数 b を初期化するとき、bool b {value}; という記法で書くとコンパイルエラーになります。この記法は型のチェックが厳しいためです。

また、Visual Studio で試すと、value を代入している箇所では警告が出ます。これは、多くの数を表現できる int型から、true か false の2通りしか表現できない bool型というように、表現力が小さくなる方向に変換しようとしているからです。このような変換を縮小変換 (narrowing conversion) といいますが、これに対して警告を出すコンパイラがほとんどです。なお、{} を使った初期化や代入の記法には、縮小変換をコンパイルエラーにする効果があります。

【上級】もし、{} を使いつつも縮小変換を許したいのであれば、bool b {value != 0}; と書く方法があります。これなら、条件式を評価した結果が bool型なので、型変換が起こらず、警告もエラーもなくコンパイルできます。

等価演算子 🔗

ここまで、ソースコードに true や false を書くか、入力によって受け取る例を挙げてきましたが、なんらかの条件を判定してみて、それが「正しい」か「正しくない」かによって論理値を取得することもできます。たとえば、「変数 value1 と変数 value2 の値が同じかどうか」を判定して、論理値を取得するといった具合です。

そのためには、等価演算子 (equality operator) という演算子を使用します。

式1 == 式2    // 左と右の値が同じ?
式1 != 式2    // 左と右の値が異なる?

== は一般的なイメージでの「イコール」にあたるものですが、C++ では「=」を2つ並べる必要があります。誤って = にすると、代入を意味することになり、間違ったプログラムになってしまうので注意してください。

!= の方は、「イコールでない」という意味になります。つまり、== とは得られる論理値が逆になります。

2つとも等価演算子として分類されています。[5]

具体的には、次のように記述します。

bool is_equal_value {value1 == value2};
bool is_different_value {value1 != value2};

== は、左右の式を評価した値が同じかどうかを判定し、同じであれば true、同じでなければ false が得られます。!= は、左右の式を評価した値が異なるかどうかを判定し、異なるなら true、同じなら false が得られます。[6]

【C言語プログラマー】C言語では int型の 0 か 1 になりました。

==!= による式を評価した時点で、true や false という値になってしまうので、次のように3つ以上の値の比較をおこなうコードは正しく動作しません。

bool is_equal_values {value1 == value2 == value3};

この場合、まず value1 == value2 が評価されて、true か false が得られます。そのため、3つ目の値の比較は、true == value3false == value3 としていることになります。

正しい方法は、value1 == value2 && value2 == value3 のように書くことです。いずれきちんと説明しなおしますが、&& が2つの条件式を結びつける役割を果たします。

【上級】左側の条件式から評価されるのは、== が(!= も)左結合の演算子だからです[7]。この例では、(value1 == value2) == value3 のように解釈されます。


実際に動くプログラムの例として、文字列を2回入力してもらい、同じかどうかを出力するプログラムを挙げてみます。パスワードやメールアドレスなどを登録するとき、同じ内容を入力させる場面がありますが、それと同様のプログラムです。

#include <iostream>
#include <string>

int main()
{
    std::cout << "Please enter the e-mail address.\n";
    std::string email_address {};
    std::getline(std::cin, email_address);

    std::cout << "Please enter the same e-mail address again.\n";
    std::string email_address_verify {};
    std::getline(std::cin, email_address_verify);

    std::cout << std::boolalpha << (email_address == email_address_verify) << "\n";
}

実行結果:

Please enter the e-mail address.
abcde@xxx.com  <-- 入力した文字列
Please enter the same e-mail address again.
abcde@xxx.com  <-- 入力した文字列
true

条件式が std::cout のところに埋め込まれると見づらいので、( ) で囲んで見やすくするのは悪くないです。あるいは、bool型の変数に結果を入れてから使うようにすれば、変数名によって意味を説明できるので、それも良い方法です。

関係演算子 🔗

等価演算子は、2つの値が「同じ」か「同じではない」のいずれかを判定するものでした。これ以外の関係性を調べるために、関係演算子 (relational operator) があります。

関係演算子で調べられる関係性とは、「左の値の方が大きい(小さい)」「左の値は、右の値以上である(以下である)」の4つです。この4つに対応して、関係演算子は4種類あります。

式1 > 式2          // 左の方が大きい?
式1 < 式2          // 左の方が小さい?
式1 >= 式2         // 左が右以上?
式1 <= 式2         // 左が右以下?

「以上」「以下」は、左右の値が同じ場合を含みます。「大きい」「小さい」は、左右の値が同じ場合を含みません。

左右の式を評価した値を比較し、それぞれの関係演算子の意味と合致していれば true、合致していなければ false になります。[8]

【C言語プログラマー】C言語では int型の 0 か 1 になりました。

比較の結果がどうなるか確認するプログラムを挙げておきます。

#include <iostream>

int main()
{
    int value {10};

    std::cout << std::boolalpha
              << (value > 0) << "\n"
              << (value < 20) << "\n"
              << (value >= 0) << "\n"
              << (value <= 20) << "\n"
              << (value > 10) << "\n"
              << (value < 10) << "\n"
              << (value >= 10) << "\n"
              << (value <= 10);
}

実行結果:

true
true
true
true
false
false
true
true

for文」のページで、for文の「条件式」のところに書いていた i < 5 のような記述の正体は「左の方が小さい?」をあらわす関係演算子です。「変数 i の値が 5 より小さい」かどうかを調べて、そうであれば true、そうでなければ false を得ていたということです。そして、true であるかぎり、ループを続行します。

for文の「条件式」には、論理値として扱えるものが置けます。そのため、関係演算子や等価演算子を使った式を書けますし、bool型の変数を置くこともできます。また、整数は bool型に変換できますから、整数を置くこともできます。以下は一例です。

for (int i = 0; i == x; ++i) { /* 繰り返すコード */ }
for (int i = 0; i != x; ++i) { /* 繰り返すコード */ }
for (int i = 0; i <= x; ++i) { /* 繰り返すコード */ }

// bool型の変数 is_equal_value があるとして
for (int i = 0; is_equal_value; ++i) { /* 繰り返すコード */ }

// int型の変数 value があるとして
for (int i = 0; value; ++i) { /* 繰り返すコード */ }

これで「n回繰り返す」使い方に限定されることはなくなりました。「ある数が入力されるまで」とか「ある変数の値と一致するまで」といった条件式でも記述できます。

まとめ 🔗


新C++編の【本編】の各ページには、末尾に練習問題があります。ページ内で学んだ知識を確認する簡単な問題から、これまでに学んだ知識を組み合わせなければならない問題、あるいは更なる自力での調査や模索が必要になるような高難易度な問題をいくつか掲載しています。


参考リンク 🔗


練習問題 🔗

問題の難易度について。

★は、すべての方が取り組める入門レベルの問題です。
★★は、自力でプログラミングができるようなるために、入門者の方であっても取り組んでほしい問題です。
★★★は、本格的にプログラマーを目指す人のための問題です。

問題1 (確認★)

次のプログラムの実行結果はどうなりますか?

#include <iostream>

int main()
{
    int value1 {10};
    int value2 {20};

    std::cout << std::boolalpha
              << (value1 == value2) << "\n"
              << (value1 != value2) << "\n"
              << (value1 < value2) << "\n"
              << (value1 <= value2) << "\n"
              << (value1 > value2) << "\n"
              << (value1 >= value2) << "\n";
}

解答・解説

問題2 (確認★)

次のプログラムの実行結果はどうなりますか?

#include <iostream>

int main()
{
    int value {0};

    std::cout << true << "\n";
    std::cout << false << "\n";
    std::cout << (value == 0) << "\n";
    std::cout << (value != 0) << "\n";
    std::cout << std::boolalpha << (value == 0) << "\n";
    std::cout << std::boolalpha << (value != 0) << "\n";
    std::cout << std::boolalpha << (value + 10) << "\n";
}

解答・解説

問題3 (基本★★)

文字列の入力を受け取り、その内容をそのまま出力することを繰り返すループを構築してください。入力内容が空文字列だったら、ループを抜け出すようにしてください。

解答・解説

問題4 (基本★★)

次の for文はどういう動作になるでしょうか?

for (int i = 0; 1; ++i) {
    // 繰り返す処理
}

解答・解説

問題5 (応用★★★)

“I am a programmer.” のように、「.」で終わる文字列があるとします。

この文字列の文字数(空白と最後のピリオドを含む)をカウントして出力するプログラムを作成してください。

解答・解説


解答・解説ページの先頭



更新履歴 🔗




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る