C++ with Windows API講習/if文とswitch文
概要
プログラムの流れを分岐させる選択文の、if文やswitch文を解説します。
重要語
式
値と演算子の並び
選択文
if文とswitch文の総称
反復文
while文とfor文の総称
制御文
選択文や反復文などの総称
式文
式にセミコロンがついたもの
空文
セミコロンのみの文
文
制御文や式文などの総称
複合文
複数の文をまとめたもの
関係演算子
<,>,<=,>=,==,!=
条件式
関係演算子と値の並び
論理演算子
&&,||,!
評価
式が計算されること
短絡評価
理論積と理論和で右辺が評価されないこと
条件演算子
条件式 ? 値A : 値B
if文
条件式に応じて文を実行する選択文
switch文
整数などに応じて文を順次実行する選択文
フォールスルー
他のラベルを跨ぐこと
break文
選択文や反復文の実行をやめる文
今回の必要語はありません。
式と文
まず、式と文について細かい定義について学びましょう。
以下のサンプルコードを実行してみましょう。
式と文
#include <Windows.h>
#include <string>
int r10() {
return 10;
}
int wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
int z = 0;
//セミコロンを付けているので純粋な値ではない
//値と見做せるもの
0; //リテラル
z; //変数
r10; //アドレス
r10(); //関数呼び出し
//セミコロンを付けているので実際は式文
//式と見做せるもの
z = 10 + r10();
//複合文
{
MessageBoxW(NULL, std::to_wstring(z).c_str(), L"wWinMainのz", MB_OK);
//複合文のzがwWinMainのzを隠す
int z = 10;
MessageBoxW(NULL, std::to_wstring(z).c_str(), L"複合文のz", MB_OK);
}
return 0;
}
式
何気なく使っている式の定義は、値と演算子の並びです。
値には、コード上の値や変数、式や関数呼び出しなどが属します。
文
C++の文は選択文や反復文、変数宣言、式文、空文などの総称です。
文は実行の順番を指定するもので、セミコロンで区切られていると文です。
選択文は今回のif文とswitch文、反復文は次回のwhile文とfor文です。
式文は式の末尾にセミコロンを付けたものです。
空文はセミコロンのみのことで、その名の通り何も処理されません。
複合文
複数の文を波括弧で囲うと、複数の文を1つの文として扱う複合文になります。
複合文の終わりはセミコロンが不要です。付けると空文が続く事になります。
複合文は複数の文をまとめる機能が主ですが、変数の見え方や寿命に影響します。
変数は宣言された複合文と、その複合文に含まれる複合文の中でしか使用できないのです。
式と文
コード「式と文」の解説です。
値と式
0;
の0
は整数リテラルです。リテラルはソースコード上の値のことです。
z;
のz
は変数名です。直前で宣言したint型の変数zのことなので値です。
r10;
のr10
は関数名です。関数名はアドレスにされるので値です。
r10();
のr10()
は関数呼び出しです。関数は値を返却するので値と見做せます。
z = 10 + r10();
のz = 10 + r10()
は式です。
ただし、セミコロンを付けると式文になるので、 全体としては式文です。
変数z
変数z
はwWinMain
関数と、それに含まれる複合文で宣言されています。
wWinMain
の変数z
は、複合文で宣言される変数z
に隠されます。
すなわち複合文の中での識別子z
は、変数z
宣言前はwWinMain
の変数z
を指します。
そして変数z
の宣言後には、複合文で宣言した方の変数z
を指すことになるのです。
条件式とbool
制御文に入る前に条件式とbool型について学びます。
以下のサンプルコードを実行してみましょう。
条件式とbool
#include <Windows.h>
#include <string>
std::wstring to_wstring(bool b) {
return b ? L"true" : L"false";
}
int wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
bool b = MessageBoxW(NULL, L"こんにちは", L"どうも", MB_YESNO) == IDYES;
MessageBoxW(NULL, to_wstring(b).c_str(), L"「はい」だったか", MB_OK);
std::wstring str, str2;
str = to_wstring(10 < 20) + L", "
+ to_wstring(10 > 20) + L", "
+ to_wstring(10 <= 10) + L", "
+ to_wstring(20 >= 20) + L", "
+ to_wstring(30 == 30) + L", "
+ to_wstring(39 != 39);
str2 = L"10<10, 10>20, 10<=10, 20>=20, 30==30, 39!=39";
MessageBoxW(NULL, str.c_str(), str2.c_str(), MB_OK);
int result
= MessageBoxW(
NULL,
L"「はい」か「いいえ」を押さないとtrue",
L"ばつ",
MB_ICONWARNING | MB_YESNOCANCEL
);
b = result != IDYES && result != IDNO;
MessageBoxW(NULL, to_wstring(b).c_str(), L"「はい」か「いいえ」を押さなかったか", MB_OK);
b = !b;
MessageBoxW(NULL, to_wstring(b).c_str(), L"論理否定", MB_OK);
return 0;
}
bool型
bool型は値にfalse
かtrue
を持つ型です。false
を0、true
を1として値を持っています。
false
は偽のことで「正しくない」、true
は真のことで「正しい」という意味を持ちます。
bool型は整数型とほぼ同じもので、ほとんどの場合で1バイトの整数として実装されます。
ただし、どんな整数を代入してもbool型が持つ値はfalse
かtrue
です。
int型とbool型
C言語との互換のため、0はfalse
、0以外はtrue
に暗黙のうちにキャストされます。
その逆も同様に、false
は0に、 true
1に暗黙のうちにキャストされます。
条件式と関係演算子
条件式は式の一種ですが、演算子には関係演算子を用いります。
条件式はbool値を結果として持ちます。
比較できる型の値値A,値B
を用いて、値A 演算子 値B
と用います。
std::wstring
は、辞書順での大小が比較されます。
関係演算子一覧
<
右辺が左辺よりも大きいとtrue
>
左辺が右辺よりも大きいとtrue
<=
右辺が左辺と等しいか大きいとtrue
>=
左辺が右辺と等しいか大きいとtrue
==
左辺と右辺が等しいとtrue
!=
左辺と右辺が等しくないとtrue
論理演算子
複数のbool値を組み合わせたり、否定をするときには論理演算子を使います。
&&
を論理積演算子、||
を論理和演算子、!
を論理否定演算子と呼びます。
論理積、論理和演算子はbool値値A,値B
を用いて、値A 演算子 値B
と用います。
論理否定演算子はbool値の値
を用いて、!値
と用います。
論理演算子一覧
&&
両辺がtrueだとtrue
||
どちらか一辺がtrue
だとtrue
!
false
ならtrue
、true
ならfalse
評価と短絡評価
式や値は評価されて値を持ちます。すなわち、評価されると演算されます。
そして、論理積と論理和演算子は短絡評価が行われることがあります。
これは右辺を評価しなくとも結果が変わらない場合に、右辺を評価しないことです。
すなわち、論理積では左辺がfalse
、論理和では左辺がtrue
であると短絡評価されます。
短絡評価された時、右辺で変数などに変更を伴う式を書いていても実行されません。
条件演算子
条件演算子は条件式 ? 値A : 値B
という構文で、条件式と値を二つ取ります。
値A,値B
は、条件式がtrue
であると値A
が、false
であると値B
が選択されます。
そして、選択された値が条件演算子を用いた式の値になります。
ほとんどの場合、値A,値B
は同じ型の値を用いります。
違う型の場合、変換によって同じ型に出来ないとエラーになります。
コード「条件式とbool」解説
コード「条件式とbool」の解説です。
to_wstring関数
std::to_wstring
関数と別に、bool値を文字列に変換するto_wstring
関数を定義しています。
条件演算子を用いて、bool値がfalse
ならL"false"
を、true
ならL"true"
を選択します。
選択した文字列は、std::wstring
に変換されて返却されます。
なお、std::to_wstring
ではboolは用意されておらず、整数として変換されてしまいます。
すなわち、false
が0
、true
が1
に変換されてしまうということです。
1、2個目のメッセージボックス
メッセージボックスを表示し、その返り値によって「はい」が押されたかを表示しています。
返り値とIDYES
が等しいかを判定し、変数b
に代入します。
そして、to_wstring
関数を用いてstd::wstring
にして表示しています。
なお、MB_YESNO
のメッセージボックスの返り値は、IDYES
かIDNO
です。
3個目のメッセージボックス
全ての関係演算子を用いた条件式の結果をまとめて表示しています。
どの関係演算子も数学と似ているので期待通りになったと思います。
また、std::wstring
は辞書順で比較されるのでした。
4、5、6個目のメッセージボックス
4個目のメッセージボックスの値をresult
に代入しています。
そして、result
がIDYES
でも、IDNO
でもなければtrue
という条件式を立てています。
条件式の結果は変数b
へ代入し、それを5個目のメッセージボックスで表示しています。
更に、変数b
の値を否定した結果を6個目のメッセージボックスで表示しています。
bool値を否定すると、false
ならtrue
、true
ならfalse
になるのでした。
if文とswitch文
それでは、if文とswitch文を学んでいきます。以下のコードを実行してみましょう。
if文とswitch文
#include <Windows.h>
#include <string>
int wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
if (true)
MessageBoxW(NULL, L"trueなので実行される", L"if文", MB_OK);
if (MessageBoxW(NULL, L"こんにちは", L"どうも", MB_YESNO) == IDYES) {
MessageBoxW(NULL, L"はいが押されました", L"結果 - 文A", MB_OK);
}
else {
MessageBoxW(NULL, L"いいえが押されました", L"結果 - 文B", MB_OK);
}
int result = MessageBoxW(NULL, L"something", L"switch", MB_CANCELTRYCONTINUE);
switch (result) {
case IDCANCEL:
MessageBoxW(NULL, L"が押されました", L"キャンセル", MB_OK);
break;
case IDTRYAGAIN: {
std::wstring str = L"が押されました";
MessageBoxW(NULL, str.c_str(), L"再実行", MB_OK);
break;
}
default:
MessageBoxW(NULL, L"が押されました", L"続行", MB_OK);
break;
}
return 0;
}
if文
if文は、if (条件式) 文A
もしくはif (条件式) 文A else 文B
という構文です。
なお、後者の構文をif-else文と呼び、区別することがあります。
条件式
がtrue
であれば文A
が、false
でかつif-else文であれば文B
が実行されます。
ただし、文A,文B
どちらも1文のみ許容されるので、複数の文を扱うときは複合文を使います。
補足 - if文のネスト
また、当然文A,文B
にif文を入れる、すなわちネストをすることも出来ます。
よって、if (条件式1) 文A1 else if (条件式2) 文A2
などというような形も出来ます。
この形だと条件式1
がfalse
の時に、else if
のif文へ実行が進みます。
なお、else if
という記述を構文と見做すことがあり、else-if文と言います。
コラム - if文の条件式の落とし穴
if文の条件式に、==
を使った条件式を指定するときは注意が必要です。
なぜなら、==
を=
とタイプミスし、代入にしてもintなどではエラーが出ないからです。
というのも、前述したようにintとboolは暗黙のうちに変換されてしまうからです。
switch文
switch文はswitch (ラベル) { 処理部 }
という構文です。
ラベルには整数などが指定できます。Windows APIの定数も実体は整数なので指定できます。
処理部
では、caseラベルとdefaultラベルを用いて分岐をします。
switch文の処理部
caseラベルはcase ラベル:
、defaultラベルはdefault:
という構文を用います。
ラベル
に指定した値と対応するcaseラベルがあるとcaseラベルに処理が移ります。
対応するcaseラベルが無く、defaultラベルがある場合はdefaultラベルに処理が移ります。
いずれかのラベルに飛んだ後は、ラベル以降の文を順次実行していきます。
フォールスルーとbreak文
switch文で気を付けたいのが、順次実行はラベルを超えても続くことです。
つまり、他の条件で実行したい文をも実行してしまうということです。
これをフォールスルーと言います。フォールスルーを防ぐには、break文を記述します。
break文はbreak;
という構文で、順次実行をやめ、switch文から抜けます。
コード「if文とswitch文」解説
コード「if文とswitch文」の解説です。
if文、if-else文
1個目のif文は条件式にtrue
を指定しているので、文A
は常に実行されます。
2個目のif文は、if-else文です。条件式は「メッセージボックスの返り値がIDYES
か」です。
すなわち、メッセージボックスで「はい」を押すと文A
、「いいえ」だと文B
が実行されます。
switch文
swtich文のラベルにはメッセージボックスの返り値を代入した、変数label
を指定しています。
メッセージボックスのスタイルにはMB_CANCELTRYCONTINUE
を指定しています。
そのため、変数label
が取る値はIDCANCEL
、IDTRYAGAIN
、IDCONTINUE
のいずれかです。
今回は、IDCANCEL
とIDTRYAGAIN
はcaseラベルで、IDCONTINUE
はdefaultラベルで対応します。
また今回は、フォールスルーは必要ないのでbreak文を忘れないようにします。
switch文と変数
swtich文の中では、原則として変数を使用することが出来ません。
ただし、caseラベルやdefaultラベルを跨がない複合文では、変数を使用出来ます。
今回は、case IDTRYAGAIN:
に複合文を置き、std::wstring
を宣言しています。
コラム - switch文の中で変数が使えないわけ
前述した通り、原則としてswitch文の中では変数を使用することが出来ません。
これは、switch文はcaseラベルかdefaultラベルに処理が飛ぶという処理に起因します。
すなわち、処理が飛んだ際に変数の宣言が飛ばされてしまう可能性があるのです。
それによって、変数を宣言していないのに破棄をすることになるのでエラーになるのです。
練習問題
Windows APIにはPOINT
構造体が用意されています。
POINT
構造体はLONG
という型の変数をx,y
と2つ持っています。
LONG
は、int型と同様に整数を扱うlong型のエイリアスです。
POINT
構造体変数のペアa,b
とx,y
について、同一の座標か判定してください。
関数に切り出しても、wWinMain
関数に直接書いても問題ありません。
また、0から5までの値を発生させるrandom
関数が用意されています。
random
関数の返り値に対応する表のメッセージを表示してください。
対応表
タイトル
本文
1
せかいせいふく
2
しぼう
3
にっぽん
4
いみふめい
default
ないです
解答の一部
#include <Windows.h>
#include <string>
#include <random>
/*POINT構造体の定義**********
* typedef struct tagPOINT *
* { *
* LONG x; *
* LONG y; *
* } POINT; *
**************************/
int random() {
std::random_device seed_gen;
std::default_random_engine engine(seed_gen());
return std::uniform_int_distribution<>(0, 4)(engine);
}
int wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
POINT a{ 50,100 }, b{ 10,50 }, x{ 10,20 }, y{ 10,20 };
int random_number = random();
return 0;
}
解答例
練習問題解答
#include <Windows.h>
#include <string>
#include <random>
/*POINT構造体の定義**********
* typedef struct tagPOINT *
* { *
* LONG x; *
* LONG y; *
* } POINT; *
**************************/
int random() {
std::random_device seed_gen;
std::default_random_engine engine(seed_gen());
return std::uniform_int_distribution<>(0, 4)(engine);
}
//変数名の表示は問題文に記載がないのでする必要はありません
void is_same(POINT* a, POINT* b, std::wstring* str) {
if (a->x == b->x && a->y == b->y) {
MessageBoxW(NULL, L"等しいです", str->c_str(), MB_OK);
}
else {
MessageBoxW(NULL, L"等しくないです", str->c_str(), MB_OK);
}
return;
}
int wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
POINT a{ 50,100 }, b{ 10,50 }, x{ 10,20 }, y{ 10,20 };
is_same(&a, &b, &(std::wstring)L"aとbは");
is_same(&x, &y, &(std::wstring)L"xとyは");
int random_number = random();
switch (random_number) {
case 1:
MessageBoxW(NULL, L"せかいせいふく", L"1", MB_OK);
break;
case 2:
MessageBoxW(NULL, L"しぼう", L"2", MB_OK);
break;
case 3:
MessageBoxW(NULL, L"にっぽん", L"3", MB_OK);
break;
case 4:
MessageBoxW(NULL, L"いみふめい", L"4", MB_OK);
break;
default:
MessageBoxW(NULL, L"ないです", L"default", MB_OK);
break;
}
return 0;
}