今週は,関数のつづきです。関数を呼び出す側と,呼び出される側とに分けて考えていきましょう。
キーワード:「関数」「引数」「戻り値」「値参照」「アドレス参照」
第3回の演習課題3を見直そう。問題は,
int a=2, b=3; int *pa, *pb; pa = &a; pb = &b;
のとき,次の二つの演算の違いを説明しなさい。
a = b; pa = pb;
であった。 ここでは,二つの命令をメモリのイメージ図で説明する。
プログラムで,確認してみること。
int a=2, b=3;
int *pa, *pb;
pa = &a;
pb = &b;
pa = pb;
printf("a = %d¥n", a);
printf("*pa = %d¥n", *pa);
次に前回の課題(4)について確認しよう。前提となるのは,関数へ渡せるのは,「数字」のみである。
(4)これまで使ってきたprintf()関数で,配列を関数に渡す方法はすでに学んでいた。アドレスとポインタ変数を参考に,関数へ配列に入ったデータをまとめて渡す方法を考え,答えなさい。
void main()
{
char a[]="abc";
printf("%s", a); /* 関数へ,文字列(文字の配列)を渡している。ここで渡しているのは何でしょう? */
}
相手に,配列を受け渡すためには,二つの方法が考えられる。
1.配列の中身を一つ一つ渡す → あまり,配列を使うありがたみがない!
2.配列のデータがしまわれている場所を教える → これだ!!
printf()関数へ文字列(文字の配列)を受け渡すには,文字列の先頭のアドレスを教えてあげれば良い。
関数へ,データを受け渡す方法には,このように,
・データそのものを渡す : a = b のイメージ
・アドレスを教える : pa = pb のイメージ
の二つの方法がある。前者を「値参照」,後者を「アドレス参照」と呼び,適宜使い分ける。
呼び出す側の都合がわかったところで,呼び出される関数では,どうなるのか考える。
値を渡されて呼び出される場合の例は,前回演習した。
#include <stdio.h>
int test(int c)
{
c *= c; /* この関数内で値を変更 */
return c;
}
void main()
{
int a, b;
printf("aの値を入力してください:"); scanf("%d", &a);
b = test(a);
printf("結果 %d ¥n", b);
}
値参照による関数の呼び出しでは,呼び出し元から関数へ変数の値がコピー,複製されて渡される。
配列を関数に受け渡したい場合などに用いる,アドレス参照では,関数に変数の格納されている「場所」を伝え, その「場所」のデータを関数内で操作するようにする。変数の格納されている場所,即ちアドレスを扱うためには, ポインタ変数を利用する。
#include <stdio.h>
int test2(int *p) /* 変数のアドレスを受け取る(ポインタ変数) */
{
*p *= *p; /* p の指す中身に変更を加える */
return *p; /* 値を返す */
}
void main()
{
int a, b;
printf("aの値を入力してください:"); scanf("%d", &a);
b = test2(&a); /* 変数aのアドレスを関数に渡す */
printf("結果 %d ¥n", b);
}
ポインタを用いて,関数に変数のアドレスを渡せることがわかった。では,同じ方法を使って,関数に配列を渡してみよう。
以下は,配列を受け渡しする典型的な例である。配列名には,配列の先頭のアドレスが格納されているので,ポインタで受け取ることができる。
配列を渡す場合は,配列の先頭アドレスだけではなく,配列の要素数を同時に渡すと,どのような要素数の配列が渡されても,関数内でうまく処理ができる。 (アドレスのみだと,受け取った関数内で配列の個数が分からない.)
#include <stdio.h>
void zero_init(int *p, int n) /* 要素数 n 個,配列の先頭のアドレス p を引数とする関数 */
{
int i;
for(i = 0; i < n; i++) {
*(p+i) = 0; /* p[i] = 0; という書き方も可 */
}
return;
}
void main()
{
int i;
int ary[] = {10, 20, 30, 40, 50};
zero_init(ary, 5);
for(i =0; i < 5; i++){
printf("ary[%d] = %d¥n", i, ary[i]);
}
}
このプログラムは,配列 ary の先頭から n 個の要素の値を 0 に設定する関数で,配列の初期化などの際に用いる。 関数には,配列の先頭要素のアドレス(ary)が渡されるので,関数側ではポインタ(int *p)で受け取っている。 この例では関数に戻り値が無いが,関数でのデータ変更は,main 関数においても反映されている。
(1)長方形の二辺の長さ(整数)を渡すと,面積を計算して戻す関数を作成し,キーボードから二辺の長さを入力すると,面積を計算するプログラムを作成せよ.
**** rectarea(***********) /* 面積の計算 */
{
return ***;
}
void main()
{
int a, b;
scanf("%d", &a);
scanf("%d", &b);
printf("面積:%d¥n", rectarea(a,b));
}
(2)配列を渡すと,配列の平均値を計算する関数を用いて,実数をキーボードから10個入力すると,平均値を答えるプログラムを作成せよ.
float average(float ***, int n) /* 平均の計算 */
{
int i;
float sum = 0;
for(i=0;i<n;i++){
sum += ****;
}
return sum;
}
void main()
{
int i;
float data[10];
for(i=0; i<10; i++){
scanf("%f", &(data[i]));
}
printf("平均値:%f¥n", average(*****, 10));
}