実践C++入門講座こんにちは。C++の文法上、文(Statements)として分岐やループ、各種宣言などなどが定義されています。分類して7種類あります。C++プログラムを書く時はこれら7種類の文を駆使します。その中でも手続き(アルゴリズム)を記述するのに用いる文が実行文です。プログラムらしい部分です。今回は主にこの実行文について解説します。非実行文である宣言文はC++のクラス定義等を行うものですので、実は仕様がたいへん大きいですから、今回は少しだけ触れて、今後徐々に解説していきます。文はC++の基本的な構成要素ですので、多数の種類があります。標準規格に沿っていますが、理解を助けるため多少独自に分類してます。;(セミコロン)を書く必要があるかないかを示してます。時々迷うと思いますので参考にされて下さい。多くは代入ですが、関数呼び出しなどもよく書きます。複数の文を{}で囲んだものです。条件分岐を行う文です。if文とswitch文があります。繰り返し処理を行う文です。次の行以外へジャンプする文です。break文、continue文、return文、goto文があります。goto文のジャンプ先とswitch文の分岐先を定義します。関数と変数と型を宣言したり定義したりします。変数を定義して初期化するものです。YESセミコロンのつけ忘れ私も非常に良くやるのですが、セミコロンを付け忘れることがあります。(私だけ?)この時、付け忘れた行にエラーが出てくれればよいのですか、残念ながら次の(コメントでない)行がエラーになります。(時には「セミコロン無し」ではないエラーになることもあります。)下記のようなコードを用意してみました。x`はセミコロンを付け忘れた文です。C++でコンパイルすると次のエラーがでます。5行目は`x=10;’です。gccでコンパイルすると次のエラーがでました。初期化忘れと言ってます。C++とgccを比べると経験上gccの方がエラー・メッセージが分かりやすいことが多いです。今回はたまたま珍しいケースにあたったようです。コンパイル・エラーを潰す時のオススメC++は構文が複雑なため、コンパイラから見ると間違った文の正しい姿は1つではなく、複数あることが多いです。その内のどれかが正しい姿だろうと想定してエラー・メッセージが組み立てられます。その想定が外れると、上記のように的確でないエラー・メッセージがでます。そこで、エラーが出た場合、その行、もしくは、それより前の行にミスがあるので、エラー・メッセージを参考にしつつ、惑わされないように心がけながらエラーを探して下さい。(この辺もC++の難しさの所以と思います。)また、エラーを潰して行く時は頭から潰すことがお薦めです。前のエラーが原因で他のエラーが出ることは非常に良くあります。出ているエラーの最後の方から潰していくとたいへん苦労します。複数出ているエラー・メッセージの最初の方から潰していくことをお勧めします。実行文は関数の中だけに書くことができます。実行文を使って「処理の手順」を記述します。;(セミコロン)を書いたものです。;だけを書いたものも式文です。これはnull文と呼ばれています。10;のように代入式ですが、代入式に限りません。foo(10);のように関数呼び出しでもよいし、’++i;’のようなインクリメント演算子とそのオペランドだけの時もあります。使いませんが、複数の代入式を,(カンマ)で区切って1つの式文にすることもできます。10;のようにint型変数iを定義して同時に10で初期化することができますが、これはint型変数iの宣言文です。上記表の「特殊な実行文」に分類してみました。2-2.ブロック(複合文){と}で囲まれた複数の文です。1つしか文を書けないところに複数の文を書きたい場合に使います。main()関数と基本の型の3.ローカル変数で説明したようにローカル変数の有効範囲も定義します。プログラムを開発する時、変数の有効範囲は必要な範囲だけに限定しておくと、変数がどこで使われているのか調べるのが簡単になりますから、よりメンテナンスしやすくなります。そのために使う場合もあります。0;}例えば、上記のようなプログラムの「:」の部分でrを使わない場合、{}ブロックを追加してその中でrを定義することも考えられます。やりすぎるとインデントが深くなって却って読みにくくなり良くないですが、変数の有効範囲を無闇に広くしないよう少し注意を払うと良いですよ。複数の処理から、条件に有ったものを1つ選択する文です。if文とswitch文があります。下記の2種類の書き方ができます。文2「条件」には通常は「式」を書きます。その式は最終的にbool型(trueかfalse)へ変換され、trueなら「文1」、falseなら「文2」が実行されます。falseの時「文2」がなければ何もしません。「式」を計算した結果が0の時はfalse、それ以外の時はtrueになります。「条件」には「初期化付きの宣言」を書くこともできます。"\n";関数foo()が呼び出されて、その結果がint型変数retへ初期設定されます。このretの値が0でないならtrueになりますので、「std::coutへの出力文」が実行されます。この時、「条件」で定義された変数は、if文の終わりまで有効です。if文を使う時の典型的なバグきっと全ての人が最低一回はやらかすと思います。次のように書いてしまうことです。書いてしまうバグです。i=3の結果は3ですから、0でないのでtrueと判定されます。「i==3の時の処理;」を常に実行してしまいます。最近のコンパイラは警告レベルを上げておけば警告してくれますので、警告に注意を払っていれば直ぐにバグを見つけることができます。警告レベルをできるだけ上げた上で、警告は全て対処して出ないようにすることをオススメします。いいつつ、今までのCMakeLists.txtは警告レベルが低いままでした。警告レベルをできるだけ上げたものは以下の通りです。警告レベルを上げたCMakeLists.txtです。下記のif_test.cpp用です。CMakeLists.txtテスト用のサンプル・ソースです。0;}if文を使う時のオススメ上記で書いた「文1」、「文2」は常にブロックにしておくことをお勧めします。・「文1」、「文2」を間違って複数の文にした時、ブロックにしてないとハマり易いです。x+=100;もともとx+=100;しかなかったところに、デバッグのためstd::coutの文を追加すると、インデントのせいで一見xが0でない時だけx+=100;が実行されそうに見えますが、ここでは既にif文が終わっているので常に実行されてしまいます。デバッグしててデバック文を入れたがために、更にバグを作りました。「マヌケ」なバグに見えますが、意外にやらかしてしまいます。気がつかないことがあるので始末に負えないです。・if文をネストした時もハマり易いです。x=y;は、インデントのせいでxが0の時xにyが代入されそうに見えますが、コンパイラは次のように処理します。x=y;}ですのでxとyが0でない時にxにyが代入されます。動作が異なりますので、頭の痛いバグになります。それをデバッグしようとstd::coutへ出力して先述のバグが発生すると目も当てられません。冗長ですが、初心者の内はできるだけif文の文1、文2は{}で括っておくことをお勧めします。2-3-2.switch文if文を特化したものがswitch文です。式の値によって処理を振り分けるものです。式==値1の時に行う処理。式==値2の時に行う処理。式が全てのcaseの値に一致しなかった時に行う処理。break;}break;に到達すると継続する行は実行されずにswitch文を抜けます。caseラベル1つにbreak文1つという制約はありません。下記のようにif文で条件判断することにより、特定の条件の時、早期にbreak;文を実行することもできます。break;}また、break;文を書かないこともできます。良く使われる記述は下記です。これは便利ですが、break;の書き忘れにはくれぐれも注意が必要です。caseラベル内に処理を書いた時は、break;を必ず書くくらいの勢いでコードを書いた方が安心です。break;を書かない方が綺麗に処理を記述出来る時は、コメントでその旨、書くことをお勧めします。時間が経って忘れてしまった後や他の人がデバッグする時などにbreak;の書き忘れを疑うとその分、無駄な時間を費やすことになりますので。値3のどれかの時に行う処理。式が値4の時に行う処理。式が全てのcaseの値に一致しなかった時に行う処理。break;}次に、defaultラベルは省略できます。省略した時、全てのcaseの値に一致しなかった時は、switch文をそのまま抜けます。最後に大事なことがあります。...は、定数式しか書けません。時々勘違いされるのですが、変数を含む式を書いてもコンパイル・エラーになります。下記のような処理はコンパイル・エラーになります。break;}そのような処理を書きたい場合はif文を使います。y==(x+2)の時の処理;}なお、結果が定数になるなら式を書くこともできます。「定数」とは、コンパイルする時に値を決定できるものです。その式へ与えるパラメータやオペランドも全て定数であることが必要です。式の途中に変数が1つでも交じるとだめです。サンプル・プログラム下記のプログラムを解読して、動作を把握してみて下さい。CMakeLists.txtを作りビルドして実行してみましょう。手順は第2回(Windows向け)と第3回(linux向け)で解説していますので、参考にされて下さい。繰り返し文は、繰り返し処理(ループ処理)を行う文です。while文、do文、for文があります。ジャンプ文は処理の流れを無条件に変更します。繰り返し処理の流れを変えたい時に用います。break文、continue文、return文、goto文があります。2-4-1.while文とbeak文、continue文while文は下記のように書きます。「条件」がtrueの間、「文」を繰り返し実行します。「条件」にはif文と同じく「式」、もしくは、「初期化付きの宣言」を書くことができます。「条件」で定義された変数は「文」の実行中有効です。「文」の開始直前に生成されて条件判定に用いられ、trueなら「文」の終了時、falseなら直ぐに破棄されます。「文」は、{}で囲まれた複合文を書くことが多いです。稀に;だけの空文のこともあります。break;でループから抜け出します。continue;は文の残りをスキップします。サンプル・プログラムビルドして実行できますので、できればデバッガでステップ実行して動作を追いかけてみて下さい。while文やbreak文、continue文の動作が良く分かると思います。while(式);まず、「文」を実行し、その後で「式」を計算します。結果がtrueの間繰り返し「文」を実行します。falseになったらdo文を終了します。break文とcontinue文を書くことができます。これらの働きはwhile文の時と同じです。do文は少しおもしろい性質を持ってます。式がfalseの時、1回だけ文を実行します。それを利用して複数の条件を判定して処理をスキップするような構造を記述するのに便利です。このような特徴を持つ構文は他にありません。この特徴を利用すると便利です。後日、プリプロセッサのマクロを解説する際に説明します。「初期化」は「式」、または、「初期化付きの宣言」を書けます。この宣言で定義された変数はfor文の終わりまで有効です。while文の時のように文を実行する度に生成/破棄されることはありません。「条件」も「初期化」と同様、「式」、または、「初期化付きの宣言」を書けます。「初期化付きの宣言」を書くことはまずありません。典型的な使い方は次の通りです。0;}「初期化」で「初期化付きの宣言」により、int型変数iを定義して0で初期化してます。10が成立するので、「文」を実行します。「文」の実行が終わったら、++iを実行してiを+1します。ここまでが1回目の処理です。続けて、「条件」が評価され、trueなら「文」を実行し、falseならfor文を終了します。break文とcontinue文を書くことができます。機能はwhile文の時と同じです。coninueは「文」の残りをスキップするので、その後で「式」を計算し、次に「条件」を評価してループの継続を判定します。全て省略することができます。){}これは、次と全く同じで単なる無限ループになります。while(true){}2-4-4.範囲ベースfor文C++11にて、上記のfor文が拡張され、範囲ベースforと言う文が追加されました。配列や動的配列、線形リスト等の要素を枚挙する時に便利なfor文です。動的配列や線形リストは標準コンテナ(STL)と呼ばれるC++の標準ライブラリにて実装されていますし、ご自身で定義することもできます。これらの解説時に範囲ベースforについても説明します。実行を中断して、指定の位置から実行を再開する文です。break文、continue文、return文、goto文があります。break文とcontinue文は既に説明した通りです。break文は、switch文、while文、do文、for文の中で使え、それらの処理を中断します。continue文は、while文、do文、for文の中で使え、実行中の文の残りの処理をスキップします。return文は関数からの戻りです。関数の解説時に説明します。2-5-1.goto文goto文は次節で説明するラベル文へジャンプします。いくつかコンパイル・エラーになるような「飛び方」があります。それ以前にgoto文を「自由」に使っては行けませんので、細かい使い方の説明はパスします。興味のある方は、ここに詳しい解説があります。goto文は何かと嫌われることが多いです。安易に使うと本当にメンテナンスし辛いプログラムがさくっと出来てしまいますからです。構造化プログラミングの手法により、ほとんど出番はなくなりました。1点だけ出番が残っています。for文などのループが多重になっている時、内側のループから複数のループを抜け出したい時だけはgoto文を使うと便利な時があります。2-5-1.goto文のサンプル・プログラムではLoopExitが「識別子」に当たります。このサンプル・ソースでは、文はnull文を書いています。switch文の飛び先は次のように書きます。機能は2-3-2.switch文で解説した通りです。switch文の中だけに書くことができます。非実行文と特殊な実行文は素の実行文と違って様々な場所に記述することができます。その宣言文の種別により書ける場所も様々です。3-1.ラベル文ラベル文はgoto文の飛び先を定義するものと、switch文の分岐先を定義するものです。goto文の飛び先は次のように書きます。ラベル文は関数の中だけに書くことができます。同じ関数内のラベル文へのみgotoできます。文3-2.宣言文いきなりですが宣言文は膨大です。C++言語仕様の大半を占めるのは宣言文と言ってもよいほどです。ここではほんの触りだけ説明します。宣言文の大半は下記のC++の3大構成要素を宣言したり定義したりします。関数はざっくりグローバル関数とメンバ関数の2種類に分けることができます。前者はクラスや構造体に所属しないもので、後者はクラスや構造体に所属するものです。両方とも関数テンプレートと言う雛形を定義しておいて空欄を埋めて具体的な関数を作る仕組みがあります。メンバ関数はstaticな関数と非staticな関数があります。変数はざっくりグローバル変数とローカル変数とメンバ変数の3種類に分けることができます。ローカル変数は関数内部で定義された関数ローカルな変数です。メンバ変数はクラスや構造体に所属する変数です。グローバル変数は関数にもクラスにも構造体にも所属しない変数です。ローカル変数とメンバ変数にはstatic変数と非static変数があります。C++14で変数テンプレートが導入されています。重要な機能ではないので当講座では解説しません。main()関数と基本の型の4.基本型についてで説明した基本型は処理系で事前に定義された型です。ポインタ、参照があります。クラスと構造体は事実上同じものです。これらにはクラス・テンプレートがあります。その他の構成要素として以下があります。頻繁に発生するわけではない、「例外」的な事象が発生した祭に、処理を中断し「例外」的な事象が発生したことを呼び出し元へショートカットで通知する仕組みです。その際に中断された処理で解放されるはずだったメモリやファイル・ハンドル等のリソースを解放する様々な仕組みがあります。名前空間、リンケージ指定、アトリビュートがあります。宣言文はこれらの全構成要素について必要に応じて次の2種類の記述を行う文です。対象の構成要素を厳密に定義する記述です。構成要素を使うために必要な情報を宣言する。対象の構成要素を使う時、詳細な定義情報までは不要な場合があります。そのために必要な最小限の情報を記述する記述です。「構成要素を定義する」の中に「変数の定義」があります。これには定義した変数を初期化する機能があります。この初期化処理は実行文でもあるため、この宣言文だけは特殊な実行文として分類しました。これはif文、while文、for文で説明した「初期化付きの宣言」のことでもあります。以下のような宣言文は全て「初期化付きの宣言」です。ret=foo();4.まとめ今回は主にC++の実行文について解説しました。処理手順は実行文を関数の中に記述することで指示します。「アルゴリズム」の記述は実行文により行うイメージです。宣言文の触りを説明しました。こちらは主に関数や変数、型を定義したり使えるように宣言したりするための文です。C++言語仕様の大半をしめるのが宣言文ですので、今後等講座の解説のほとんどは宣言文についてとなります。ところで、個人的にちょっと意外だったのですが、ラベルも文であることに驚きました。;を書かないといけない場合があることに違和感を感じていたのですが、めったに使わないのでついサボって調べてませんでした。今回、解説を書くために調べてやっと納得できました。来週は、関数、そしてC++で取り扱う4大メモリの1つスタックについて解説します。ビルドを開始しました。(既定のターゲット)。(既定のターゲット)。