まずは次のソースを見てください。
#include <stdio.h>
void hoge(void)
{
int i;
for(i=0;i<3;i++)
{
printf("ここはサブルーチンです\n");
}
return;
}
int main(void)
{
printf("いまからサブルーチンに入ります\n");
hoge();
printf("戻ってきました\n");
return 0;
}
いつものint main(void)の前になにやらあやしげなものが加わりました。
これがサブルーチンです。
実行の流れはどうなっているのかというとまず普通にint main(void)からスタートします。
そしてhoge();のところに来ると処理がvoid hoge(void)に移り{}内が実行され、{}内の最後まで行くかreturn;が実行されるとmainの方に戻ってきてmainの続きが実行されます。
簡単に言えばhoge();のところにvoid hoge(void)の中身が書かれているのと一緒です。
プログラムの一部を別のところに書くことによってmainを簡略化したというわけです。
しかし、サブルーチンを使うもっと大きなメリットがあります。
void hoge(void)内の処理をhoge();と書くだけでどこからでも何度も呼び出せることです。
この例ではhoge();を一回しか使ってないのでそこに中身を書いたのと変わりませんでしたが、void hoge(void)内の処理をプログラム中のあちこちで使う場合、しかも{}内がもっと長かった場合、いちいち同じことをそれぞれのところに書いていたのでは大変です。
サブルーチンなら一回中身を定義してしまえばその必要はないというわけです。
では使い方です。
まずmainより上に
void サブルーチン名(void)
{
中身
}
を書いておき、これを使う時は
サブルーチン名();
です。
なぜmainより上かというとサブルーチンを呼び出すhoge();の行より前にその中身が書かれていなければならないからです。
宣言だけ先に書いておけば中身はどこに書いてもよくなります。
#include <stdio.h>
void hoge(void);
int main(void)
{
printf("いまからサブルーチンに入ります\n");
hoge();
printf("戻ってきました\n");
return 0;
}
void hoge(void)
{
int i;
for(i=0;i<3;i++)
{
printf("ここはサブルーチンです\n");
}
return;
}
こっちもまずはソースを。
#include <stdio.h>
int yakusuu(int x)
{
int i,j=0;
for(i=1;i<=x;i++)
if(x%i==0) j++;
return j;
}
int main(void)
{
int a;
a=yakusuu(20);
printf("20の約数は%d個です\n",a);
a=yakusuu(360);
printf("360の約数は%d個です\n",a);
a=yakusuu(6000);
printf("6000の約数は%d個です\n",a);
return 0;
}
サブルーチンと違って変数の受け渡しがありますがほぼ同じなので分かるでしょう。
宣言の()内に書いた変数に呼び出す時の()内に書いた値(引数)が代入され、returnで処理が戻ると同時にreturnの後ろに書いた値が返り値となります。
あとはサブルーチンの時と同じです。
使い方は
返り値の型 関数名(引数の型 引数を受ける変数名)
{
中身
}
呼び出す時は
返り値を受ける変数=関数名 (引数);
数学の関数のようになにか値をいれるとそれに応じた値が返ってくるというものがつくれます。
なお関数名(引数)と書いた部分自体が返り値の値を表しているので例えば上の例ならばいちいち変数に受けなくても
printf("20の約数は%d個です\n",yakusuu(20));
とやることもできます。
関数によっては引数のないものも存在します。
その場合は引数の型をvoidにして
返り値の型 関数名(void)
{
中身
}
とやり、呼び出す時は
返り値を受ける変数=関数名();
となります。
関数によっては返り値のないものも存在します。
その場合は返り値の型をvoidにして
void 関数名(引数の型 引数を受ける変数名)
{
中身
}
とやり、呼び出す時は
関数名 (引数);
となります。
関数によっては引数も返り値もないものも存在します。
鋭い人は気づいているかもしれませんが、これがさきほどサブルーチンとして説明したものです。
関数は数学的な関数としての性質(値を受け返す)とサブルーチンとしての性質(処理の一部をわけて書く)を兼ね備えているというわけです。
ていうか、普通はわざわざ別扱いせず全て関数として教えるものです。
ただ、そうすると数学の関数のイメージを持ってしまい、プログラムの一部としての関数という概念が理解しずらいという弊害が発生しそうなのであえてサブルーチンから説明してみました。
両方の性質を兼ね備えた例としては例えばさきほどのyakusuu関数に
printf("約数計算中…\n");
という行を加えたとして
if(yakusuu(a)==2)
のように素数かどうかの判定に使うとすると、ifの行にきたところで表示も行われます。
つまりifの中の条件を調べる時、関数が呼び出されて処理がそちらに移りprintfの行も実行され、その後mainに戻る時に返した値によって条件分岐が行われるということです。
条件判定の最中にも行って戻ってくるということが行われています。
決して値の受け渡しのみが行われるのではありません。
関数によっては引数が複数なものも存在します。
その場合()内に,で区切って複数の引数を書きます。
返り値の型 関数名(引数の型 引数を受ける変数名,引数の型 引数を受ける変数名,…)
{
中身
}
呼び出す時は
返り値を受ける変数=関数名 (引数,引数,…);
返り値を複数にすることはできません。
変数は各関数で独立です。 つまり変数名が同じでも違う関数内なら別の変数として扱われ宣言も個別に行う必要があります。 また、同じ関数でも呼び出されるたびに変数はリセットされます。 これにより関数をつくる時に別の関数と変数名がダブることを気にせずに変数名を決めることができます。 このような変数の有効範囲や寿命をスコープといい、普通は宣言された関数内のみ有効でその関数が終わると寿命がつきます。
関数の外(#include <stdio.h>などを書くようなところ)で宣言された変数はグローバル変数といいスコープがソース全体になります。