関数
プログラムが長くなると、一から全て組むのは大変です。
このような場合、ある機能をもった関数ごとにわけてやれば、他の人と分担して作業することができます。
こうすることで、他の人が作った関数の内部構造を知らなくても、その機能だけ知っておけばよいし、
また、他のプログラムで、その機能だけ使いたいときにも使いまわしすることができます。
関数の作り方ですが、次のプログラムを見てください。
<関数>
#include<stdio.h>
Function() /* 関数名Functionはmain以外の名前なら何でもいいです */
{
printf("hello\n");
}
main()
{
Function(); /* Function関数を呼び出している
*/
printf("end\n");
} |
<実行結果>
このプログラムでは、いつもの main 関数の他に、新しく Function 関数をつくりました。
Function 関数の名前は、main 以外なら何でもいいです。
プログラムは、まず main() 関数から実行されるので、main 関数内をみてみましょう。
すると、main 関数内で、Function 関数が呼び出されています。
コンピュータは Function 関数が呼び出されたので、その Function 関数にとびます。
Function 関数内では、printf 関数によって、画面に hello と表示しています。
Function 関数の処理が終わったので、main 関数の呼び出された場所に戻ります。
戻った場所、つまり main 関数内の Function(); の次の処理から再開されます。
printf 関数で画面に end と表示させてプログラムが終了します。
ここで、注意なのですが、自作した関数は必ず main 関数の前に書いてください。
なぜなら、コンピュータは上から順番にしか読んでくれないので、
後にかくと、main 関数内で Function 関数が呼ばれても、
コンピュータにとって、“そんな関数はないよ”となるからです。
もし後に書く場合は、次のように書いてください。
<関数2>
#include<stdio.h>
Function(); /* 関数Functionは後にあるよと宣言している */
main()
{
Function(); /* 後にあるFunction関数を呼び出している */
printf("end\n");
}
Function()
{
printf("hello\n");
} |
これは最初に書いたプログラムと同じものです。
つまり、main 関数が始まる前に、“Function() は後にかいてあるよ”と宣言しているのです。
どちらの書き方でもいいですが、どちらでもわかるようにしててください。 |
引数(ひきすう)を持った関数
例えば数学の関数 f=ax を考えてみましょう。
このとき f の値は、a と x を指定してやれば f の値が決まりますね。
これと同じように、c言語の関数も、ある値によって関数の値が決まるようにすることができます。
次のプログラムを見てください。
<引数を持った関数>
#include<stdio.h>
int f(int,int); /* 関数の宣言では引数は型だけでもOKです
*/
main()
{
int z;
z=f(2,5);
printf("2×5=%d\n",z);
}
int f(int a,int x)
{
int ans;
ans=a*x;
return ans;
} |
<実行結果>
このプログラムは、数学の f=ax を関数にしてみました。
a に 2 を、x に 5 を入れたときの結果です。
まず、main 関数の後にある int f(int a,int x) から見てみましょう。
これは、関数 f が、int 型の a と、int 型の x を関数の外から受け取ることを意味します。
数学の関数と同じように、
関数 f は、外部から a と x の値を指定しなければ何の値も持たないということです。
そして、受け取った a と x の値(この場合a=2,x=5)を使って、関数 f は処理されます。
関数 f 内の ans=a*x; によって、計算された結果が ans に代入されます。
return ans; によって、ans の値(ans=10)が関数 f の実行結果となるのです。
ここで、int f(int a,int x) の一番最初についている int は、関数 f の型で、
関数 f の実行結果と同じ型でなければなりません。
この場合、return で返される値が int 型の ans なので、
当然、関数 f も int 型にしなければならないのです。
次に、#include<stdio.h> のすぐ後ろの int f(int,int); ですが、
これは、先ほどと同じで、コンピュータに“f は後にあるよ”と宣言しているのですが、
( ) 内には、型だけ書いていればよく、変数名は書かなくてもよいですし、異なっていてもかまいません。
つまり、int f(int,int); の部分は、int f(int i,int j); と書いても問題ありません。
int f(int a,int x); の int a や int x のことを引数と言うのは第一章で説明しましたが、
とくに、関数本体の int f(int a,int x); の引数を実引数といい、
最初に宣言する int f(int,int); の引数を仮引数といいます。
先ほど説明したように、最初に宣言するときは“仮”引数だから変数名はなんでもいいのです。
getchar,putchar で、関数まで勉強すると、() の中に何も書かない理由がわかるようになる。
と述べたのは、getchar ,putchar は引数を持たない関数だから。という理由です。 |
値による呼び出し と 参照による呼び出し
関数の引数へ値が渡されるとき、c言語には2通りの方法があります。
一つは値による呼び出し(call by value)と呼ばれる方法で、受け取る関数は、その値をコピーします。
もう一つは参照による呼び出し(call by reference)と呼ばれる方法で、
受け取る関数は、その値のアドレスを受け取ります。
直訳により、値による呼び出し,参照による呼び出しという言い方をしてますが、
関数内から呼び出すというイメージより、値渡し,参照渡しというように、
関数へ値を渡すと考えた方がわかりやすいと思います。
<値による呼び出しと参照による呼び出し>
#include<stdio.h>
func(int,int *);
main()
{
int x,y,*p;
x=10;
y=15;
p=&y;
printf("<前>x=%d\n",x);
printf("<前>*p=%d\n\n",*p);
func(x,p);
printf("<後>x=%d\n",x);
printf("<後>*p=%d\n\n",*p);
}
func(int a,int *b)
{
printf("<func>a=%d\n",a);
printf("<func>*b=%d\n\n",*b);
a=a*2;
(*b)=(*b)*2;
} |
<実行結果>
<前>x=10
<前>*p=15
<func>a=10
<func>*b=15
<後>x=10
<後>*p=30 |
上のプログラムを見てみると、関数 func で x と *p の値は2倍されて、main
に戻ってくるように見えます。
ですが、実際は<実行結果>の通り、x は変化せず *p の値だけが2倍されています。
これは、関数の受け渡し方法の違いによるものです。
x の受け渡し方法は call by value つまり、値を関数内でコピーしています。
*p の受け渡し方法は call by reference つまり、関数はアドレスを受け取っています。
この違いにより、関数内で計算は
x の値をコピーした a が2倍されるだけで、x は何の影響も受けません。
*p のアドレスを受け取ったので、*b のアドレスが *p のアドレスと同じになります。
つまり、*p と *b は同じアドレスなので同じ値を扱うことになり、*b の変化は
*p にも影響します。
上の説明でわからない方は、
関数の受け渡しはすべて call by value だと思って良いと思います。
上の x のような場合は“値をコピーする”で、call by value そのままです。
*p の場合も“アドレスをコピーする”ということで、 call by value と同じことじゃないかと思っています。
そうすると、アドレスをコピーしてるので、そのアドレスの指している値は1つとなり、
値を変更しても、アドレスは変更しないので、
そのアドレスで中身を見ると、値が変更されていることになります
|
|