第一章:基礎知識
4.関数
C言語では関数と呼ばれる一つの処理をまとめる機能があります。これを利用することで、任意の処理を実行したり、変数の値を編集したりできます。 C言語では main関数からプログラムが開始されるというルールがあります。C++Builderでも同じように出来ていて、プロジェクトマネージャのProject1.cppというファイルをダブルクリックして開いてみてください。次のようになっていると思います。
WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { try { Application->Initialize(); Application->MainFormOnTaskBar = true; Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; }
表記が多少違いますが、C++Builderでも_tWinMainという関数からプログラムが開始されるようになっています。 ここではmain関数以外の関数を自分で作成して利用する方法を解説したあと、既存の標準的な関数をいくつか紹介します。
単純な関数の例を示します。これが基本的な関数の書き方になります。
void func() { Form1->Caption="関数テスト"; }
この関数を書く場所ですが、Unit1.cppの中に、TForm1 *Form1;という部分があると思います。このすぐ下に書き込んでください。そして、フォームにボタンを一つ配置して、ボタンのクリックイベントに以下のように記入してください。
void __fastcall TForm1::Button1Click(TObject *Sender) { func(); }
これで、ボタンを押すと、func()という関数が実行され、タイトルバーの文字が 関数テスト という表示になります。 関数の書き方から見ていきます。まず、void という表記がありますが、これは値を返さない関数の場合に設定する物になります。続いて、funcですが、ここには好きな名前を自由に付けることが出来ます。ただし、名前付けのルールは変数と同じで、数字を先頭に持ってくることは出来ませんし、記号を名前に含めることも出来ません。 次に、( ) がありますが、ここには関数に値を渡したいときに変数名を入力しておくことで、そこを経由して関数に値を送り込むことが出来るようになります。この括弧の中に書かれる、値を受け取る変数のことを引数(ひきすう)と呼びます。では、以下のように入力してください。
int func(int x) { int i; i=x*2; return i; } void __fastcall TForm1::Button1Click(TObject *Sender) { Caption = func(10); }
これは、引数にint型の整数を受けて、その数字を2倍にして返してくる関数の例です。 func(10)は戻り値がint型なので、整数を戻してきます。戻す整数はreturn i;の部分のことで、iの値がCaptionに表示されることになります。
void func1(int x) { x=x*2; } void func2(int *x) { *x=*x*2; } void __fastcall TForm1::Button1Click(TObject *Sender) { int x=10; func1(x); Caption = x; } //--------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { int x=10; func2(&x); Caption=x; }
func1とfunc2の違いは引数にポインタを利用しているかどうかの違いだけです。内容はxの値を2倍するだけですが、func2は正常に動作しますが、func1では実行してもxの値が変化しません。何故かというと、関数の引数に与えられる変数は、ポインタではない場合、引数に入力した変数のコピーが利用されるからです。 コピーが利用されるということは、変数のアドレスが違うということです。なので、コピーをいくら変更しても元のxに何ら変化は起こらないと言うことです。ポインタを置くことで直接アドレスにアクセスできるので、値を編集することが出来るようになります。 引数に変数を渡すことで直接値を編集する書き方がまた別にあります。それが以下の方法です。
void func3(int &x) { x=x*2; } void __fastcall TForm1::Button1Click(TObject *Sender) { int x=10; func1(x); Caption = x; }
こちらでは、引数にint型の普通の変数のアドレスを取得するときのマークが付いています。この方法では、関数内の式にポインタのマークを入れる必要がなく、また、利用するときも、わざわざポインタを渡す必要がありません。 これはどういうことかというと、この引数に普通の変数を渡すと、関数は渡された変数のコピーを作成せず、渡された変数のアドレスを参照し、関数の中では普通の変数の扱いになるので、ポインタのマークが必要なくなります。これをアドレス渡しと言います。
ちなみに、値渡しの場合、メモリ上にコピーを作成するのでサイズの大きな物を渡したり、関数を何度も呼び出す場合は、コピーでメモリを確保するので、動作が非常に遅くなる原因になります。参照回数の多い変数や、大きな変数を利用する時はポインタ渡しかアドレス渡しで渡すようにしましょう。
関数を宣言するには利用する場所よりも前に書かなくてはいけないというルールがあります。次の通りに入力してください。
void __fastcall TForm1::Button1Click(TObject *Sender) { Caption = func(10); } int func(int x) { int i; i=x*2; return i; }
func関数はButton1Click関数の下に作成しました。そして、Button1Click関数の中でfunc関数を呼び出しています。実行してみてください。以下の画像のようなエラーが出たと思います。
利用しようとする関数が自分よりも前に存在しないので、関数が見つからず、未定義の関数としてエラーが出されます。
このようなことが起きてしまうので、利用する関数より前に利用される関数を書く必要があります。
しかし、これを回避する方法があります。次の例を見てください。
int func(int x); void __fastcall TForm1::Button1Click(TObject *Sender) { Caption = func(10); } int func(int x) { int i; i=x*2; return i; }
Button1Click関数の前に、func関数の内容がない名前だけが書かれた行が追加されています。
これは、この宣言をした以降のどこかに、この関数と同じ形式、同じ名前の関数の実態がありますよ。という事をコンパイラに知らせて関数が見つからないということ回避する為のものです。これを、関数のプロトタイプ宣言と言います。
それではC++Builderに標準的に搭載されている関数を紹介します。
まずは、char型配列に文字列を書き込むstrcpy関数ではなく、StrCopy関数を紹介します。
実は、strcpyはC言語で標準的に用意されている汎用の関数なのですが、StrCopyはC++BuilderのVCLで標準的に用意されているstrcopyと同等の関数になっています。VCLは元々C++Builderの兄貴分のDelphi用のライブラリで、この関数もDelphi用に用意された関数です。今回はせっかく用意されているのでVCLのStrCopyを利用します。
void __fastcall TForm1::Button1Click(TObject *Sender) { char src[]="12345"; char buff[256]; StrCopy(buff,src); Caption=buff; }
StrCopy関数は文字列のコピーに利用する関数で、引数にはコピー先とコピー元のchar型のポインタを指定します。コピー先はあらかじめ、サイズを指定しておく必要があり、ここでは256文字格納出来る配列を作成しました。
StrCopyを実行するとbuffにsrcの内容がコピーされます。表示すると12345がそのまま表示されます。
次にコピーされる時の特徴がよくわかるサンプルを示します。
void __fastcall TForm1::Button1Click(TObject *Sender) { char src1[]="1234567890"; char src2[]="abcd"; char buff[256]; StrCopy(buff,src1); StrCopy(buff,src2); Caption=buff; }
StrCopyを2回実行していますが、最初に実行する方が、長い文字列になっています。
しかしコピーなので、2回目のStrCopyを実行したらbuffに格納される文字列は、abcd になります。
実行してみると、Captionに表示される文字は、abcd だけになっていると思います。
コピーとしては正常にコピーされていると思いますが、コピー先のbuffがどうなっているかというと
abcd_67890_
という形でコピーされています。アンダーバーでNULL文字を表現してみました。見えない文字NULL文字も一緒にコピーされているということです。
確認するためにCaption=buff;の所をCaption=buff+5;として実行してみてください。67890と表示されたはずです。つまり、StrCopyを複数回実行してもbuffの内容の空いている部分がすべて空きに変化するわけではなく、必要な箇所だけがコピーされるということです。Captionに代入するとき、StringクラスはNULL文字までを読み込むので、最初のNULL文字が確認された位置までが読み込まれることになります。
次に、char型の配列の文字列を連結する関数を紹介します。StrCat関数です。C言語標準の関数名ではstrcatです。
void __fastcall TForm1::Button1Click(TObject *Sender) { char src1[]="1234567890"; char src2[]="abcd"; char buff[256]; StrCopy(buff,src1); StrCat(buff,src2); Caption=buff; }
StrCatは最初の引数に先頭に来る文字列配列を指定し、2番目の引数に後ろに接続する文字列配列を指定します。サンプルの実行結果は1234567890abcdになります。StrCatを使うと、最初の引数の文字列配列のNULL文字を除外してそこから後続の文字列を連結します。
最初の引数の配列のサイズは連結後の文字列よりも大きいサイズを用意しなければ行けません。
関数のマニュアルはWeb上で確認できます。ここには書き切れないほどの関数の説明を見ることが出来ます。
URL:http://docwiki.embarcadero.com/Libraries/XE4/ja/System.SysUtils