関数の戻り値を利用してみる
〜if 文、戻り値(返り値、Return Value)
〜if 文の講義ではないけれど・・・
さて、if 文は C 言語をやる上で一つの章を成します。if を解説するつもりはなかったのですが、ついでなので解説を加えてあります。仕方なく講義調になりましたがご容赦を。あまり講義調になると、いたずらにつまらなくなるだけなのです。 if 文は簡単、と思っていても、そのことが落とし穴になる ことはないと思います。
使ってみるのでついてきてください。
使って分かるというのが本来のありかたです。
#include <windows.h>
について少し説明します。
#include は、インクルード指令といって、プリプロセッサに対する命令です。(プリプロセッサ命令)
include は「含む」という意味ですが、文字通り、"windows.h" というファイルを、この位置に、「丸ごと」含めてしまう命令です。「プリプロセスの過程で含めてしまう」 ということになります。拡張子は、.h に限らず、かりに自分で「丸ごと含めたい」ファイルがあるならば、それをインクルードしても構いません。.cpp をインクルードしてしまっても、いっこうに構いません。ときにはやることです。ただ、(自分の)整理のために、濫用は禁物です。常識的には、.h のファイルをインクルードすることにします。
定数や構造体の型、関数の型などが書かれている .h のファイルは、ヘッダファイル(Header file) といって、「プログラム内である関数を使用したら、その関数のプロトタイプが記されているヘッダファイルを自分で探して」 #include します。( しなくてはなりません)。 C 言語では (または他の言語でも)、このようなスタイルでプログラムを組みます。「インクルード」はプログラミング一般において、常識的なことです。
どの関数を使ったら、どのヘッダをインクルードすればいいのか、を調べることははじめは面倒なことです。それを知るには、「使いたい関数に関して調べてみる」ことです。関数の使用法といっしょに、「必要なヘッダファイル」 とか、「定義されているヘッダファイル」 といったぐあいに説明されています。(静的)関数一般に関しては、ヘッダファイルに、プロトタイプが宣言されていて、実体は、スタティックライブラリにあります。「コンパイラ」というものは、関数にかんしての情報は、この「2つ」にまったく「たよりきり」になるわけです(面倒だからいっか?)。勉強したければ、C:\borland\bcc55\Include ディレクトリにヘッダファイルがありますので、直接見てみると良いと思います。その際、grep.exe ユーティリティなども活用できるでしょうか。
----------------------------
尚、Windows API 関数に関しては、"windows.h" をインクルードしておけば、かなりの関数(と構造体) がこれだけで使用可能です。また、無駄なファイルをインクルードしても、そのファイルが C 言語文法上問題がなければ、問題は起こりません。
さらに、C 言語では
関数のことだけ気にしていれば問題ありませんが、Windows API では、「Windows API 構造体」の使用にも、それを定義するヘッダファイルが必要となることも言っておきます。
ヘッダファイルを仮に忘れたまま関数や構造体を使うと、「未定義の関数 xxx 」 「未定義のシンボル xxx」 というコンパイル時のエラーになります。つまり、このエラーのときは、インクルードファイルについて確認してみましょう。インクルードがしっかりできていても、(関数の)スペルを間違えている場合や、引数の数や用いた変数の型が間違えている場合もあります。ややこしいところです。頑張ってください。#include 行のあとに、余計な 「;」 があったりしても、インクルードしたつもりでも正常に #include されていない、ということがよくあります!
例えば前ページのソースコードでは、関数は、 WinMain() 関数と MessageBox() 関数だけだから、C のヘッダは必要なく、Windows API のヘッダ、"windows.h" だけで済んでいます。#include <windows.h>。 この場合でも、さらに C 言語の関数を一つでも使えば、C 言語のヘッダファイルも必要です。
さて、#include 指令には2つ書き方があります。< と " の違いです。
#include <windows.h>
#include "C:\borland\bcc55\Include\windows.h"
<> でくくる場合
は、インクルードパスの通っている場所にあるファイルを指定する場合です。( bcc32.cfg または、コンパイラオプション -I に関して参照しましょう )"" でくくる場合
は、フルパスで指定したい場合です。また、コンパイルの際のソースファイルと同じ場所にあるファイルならば、「ファイル名のみ」 でも指定可能です。
補足 : C++ のヘッダファイルをインクルードする際は、.h を省略して、例えば stack.h なら、#include <stack> とします。
以上が、#include 指令 の説明です。
・・・長かったですね。ここからは以上のこととは別のことです。切り替えて考えましょう。
(フォントを楽しむことをお許しください。)
//
// program "challange2.cpp"
// bcc32 -tW challange2.cpp
#include <windows.h>
int WINAPI WinMain( HINSTANCE hCurInst, HINSTANCE hPreInst,
LPSTR lpszCmdLine, int nCmdShow )
{
int ret ;
ret = MessageBox( NULL , "
このプログラムでは、まず、一番初めにメッセージボックスが出てきます。
MB_OKCANCEL というのが 今回、メッセージボックスに対するあたらしい指定で、前回は [OK] ボタンだけだったところ、今回は、メッセージボックスに、[OK] か [キャンセル] かの選択をうながす 2つのボタンが付きます。その選択の結果が、「戻り値」として 関数から戻ってくる わけです。
どちらかのボタンを押すまでは、このアプリケーションのプロセスはここで止まったまま、一時停止です。だから、プログラム内でも、ボタンが押されるまで 「MessageBox() 関数が戻らない」というように考えます。MessageBox 関数が戻ると、止まっていたプログラム内の流れが再開するかのように、次の 「= 代入」 へ・・・と次々続いていきます。
ちなみに、MB_APPLMODAL というのは、前回もこの指定を使いましたが、この、メッセージボックスのところで制御を止めるためのものです。普通、他の大多数の関数は、その関数を実行したら、制御が戻るまでプロセスは進行しません。けれども、MessageBox 関数はその限りではなく、少し便利に出来ていて、特別なのです。そこで、敢えて MB_APPLMODAL を指定することで制御をとめているのです。
[OK] か [CANCEL] ボタンが押されると、int 型の変数 "ret" に、戻り値 (Return Value) が格納(代入)されます。 演算子 = で "ret"に 代入しています。
戻り値は、[OK] ボタンが押されると
IDOK、[CANCEL] ボタンなら IDCANCEL ですが、実際のところは、IDOK は数字 の 1、IDCANCEL は数字の 2 です。つまり、上のプログラムでは、 if( ret == 1 )... としてもまったくおんなじです。if は、「もし」 という意味です。
これら IDOK、IDCANCEL が数字のかわりに使えるのは、windows.h をインクルードしたことによります。ところが、実際には、windows.h には記述(定義)されておらず、winuser.h に定義されています。windows.h をインクルードすると、内部ではさらに winuser.h がインクルードされているので、結果、「windows.h だけのインクルードで済む」ということになっています。
関数がどのような「値」を返すのかは、それぞれの 「関数の説明」が説明するべき事柄です。関数によって、エラーが起きたか起こらなかったかを返してくる場合もあれば、今回のように、「結果」 を返してくる場合もあります。MessageBox() 関数を調べれば、他にも、IDNO(==7) とか、IDYES(==6) なども定義されていることが分かります。これらは、winuser.h に定義されていますが、たいてい、「関数」が使えていれば、関係する周辺の「定数」も使えるようになっているで、ヘッダファイルのことはあまり心配しなくても大丈夫です。
さて、if の意味は「もし」、としか説明しないなんて、手抜きもいいところでしょうか・・・。if のことにこだわると、はじめは確かにややこしいですね。
if の括弧 () の中身は、0か、そうでないか(1とか、ほかの数) が重要です。
本来的に、if( 1 ) .... ; if( 0 ) .... ; のようにイメージするのがわかりやすい。if( 1 ) なら実行されます。if( 0 ) の場合実行されません。でもだったら、上の例で、if( ret == IDOK ) .... ; となっています。これはいったい何だ! と。なりますね。
説明を試みれば、「ret == IDOK」 が、成・り・立・つ・ と( ret イコール IDOK ならば、)、「ret == IDOK」 そのものそれ自体が、そっくりそのまま、
0以外の数 (non-zero : ノンゼロ ) となる(評価される)から、if( 1 ) と同じことになります。( if( 2 ) でも if( -1) でも同じですが。)逆に、成りたたない場合は、「ret == IDOK」 の部分は、
0 となり、if( 0 ) となるから、if の括弧の中身が 0 だと実行されないので、今回の例では、 else 以降に回される(制御が移る)のです。else がなければ、単にスキップされます。
今回、「ret == IDOK」 の値が、動的に変化するから if 文としての価値があるのであって、コード中で、if( 0 ) ... ; とそ・の・ま・ま・記述すれば、その部分は常に実行されないし、if( 1 ) ... ; とすれば、その部分は常に実行される、ということになります。コンパイラも、「条件が常に真」とかいって警告してくれます。そんなものはもとからして意味のない条件文になるわけです。
そして、if( ret == IDOK ) ... ; を日本語に訳すのであれば 「もし、ret が IDOK と同じ (==) なら」と、日本語に訳せますね。それでいいんだと思います。
if( !1 ) はどうなるのでしょうね?if( !0 ) は?
・・・だから。こだわると大変だと言っています。(自分に向かっていってるの?)
! 1 は 0 に等しいです。 ! 0 は、non-zero です。
「!1 == 0 は non-zero」 、「!1 != 0 は 0 」 です。
トップ インデックス [前へ] [次へ]