<値渡しとアドレス渡し>
関数への引数の渡し方には「値渡し」と「アドレス渡し」があります。引数として、変数に&をつけたものや、配列名、ポインタを渡すケースは、みなアドレス渡しになります。
アドレス渡しは、変数渡しとどこがちがうのでしょうか?一言で言えば、「呼び出し元の変数の中身を、関数側で書き換えることができる」という点です。
つまり、引き出しの例えを使えば、値渡しとは「引き出しの中身だけを関数に渡す」(関数側からは、もともと何番の引き出しに入っていたものかは分からない)のに対して、アドレス渡しは「引き出し番号を関数に伝えて、その引き出しに計算結果をいれてもらう」方法というわけです。
(例)
#include
<stdio.h>
void
summation(int, int, int*); /*
関数summationのプロトタイプ宣言 */
int
main(void) {
int
n=2, m=3;
int
a=0;
/*
main関数内の各変数の値とアドレスを表示 */
printf("variables
in \"main\" (before function call)\n");
printf("
n is %d @ %p\n", n, &n);
printf("
m is %d @ %p\n", m, &m);
printf("
a is %d @ %p\n\n", a, &a);
summation(n,
m, &a); /*
nとmは値渡し、aはアドレス渡しでsummationを呼び出し */
/*
summation呼び出し後のaの値とアドレスを表示 */
printf("variables
in \"main\" (after function call)\n");
printf("
a is %d @ %p\n", a, &a);
return
0;
}
/*
関数summationの定義 */
/*
nnとmmの値をaaが指すアドレスに格納 */
void
summation(int nn, int mm, int *aa) {
*aa
= nn + mm; /*
aaが指すアドレスにnnとmmの和を代入 */
/*
関数summationの中での各仮引数の値とアドレスを表示 */
printf("variables
in \"summation\"\n");
printf("
nn is %d @ %p\n", nn, &nn); /*
nnの値とアドレス */
printf("
mm is %d @ %p\n", mm, &mm); /*
mmの値とアドレス */
printf("
*aa is %d @ %p\n\n", *aa, aa); /*
aaが指す変数の値とaa自身の値(指している変数のアドレス) */
}
上の例では、関数summationを、始めの二つの引数(n, m)を値渡し、3つめの引数(&a)をアドレス渡しして呼び出しています。
呼ばれた関数側は、アドレスを受け取るわけですから、ポインタ変数を仮引数としておく必要があります。mainから呼び出された時に、このポインタ仮引数にあるアドレスが代入されます。上の例では、summation関数内のポインタaaに、呼び出し時にmainの中の変数aのアドレスが入れられます。従って、関数内部でこのアドレスに計算結果の値を書き込む(*aa = nn+mm;)と、mainのなかの変数aの値が書き換わることになります。
このプロクラムを実行すると、例えば以下のように表示されます。mainの変数aのアドレス(0xbffffaa8)が、summation関数内のaaに渡されている様子が分かります。
(実行結果の例)
variables
in "main" (before function call) main内での変数(呼び出し前)
n is 2 @ 0xbffffaa0 ← nの値とアドレス
m is 3 @ 0xbffffaa4 ← mの値とアドレス
a is 0 @ 0xbffffaa8 ← aの値とアドレス
variables
in "summation" summation関数内の変数
nn is 2 @ 0xbffffa78 ← nの値をnn格納(nとは別アドレス)
mm is 3 @ 0xbffffa7c ← mの値をmmに格納(mとは別アドレス)
*aa is 5 @ 0xbffffaa8 ← aのアドレス(aa)に計算結果を格納
variables
in "main" (after function call) main内での変数(呼び出し後)
a is 5 @ 0xbffffaa8 ← 関数呼び出し後はaの値が変わっている。