C言語において関数呼び出しの際の引数の受け渡しは値渡し (call by value)であり、変数の値をコピーしてから関数を呼び出すため、関数の中で変数の値を書き換えても呼び出し側の変数は影響を受けない(書き換わらない)。
例えば、(ポピュラーな例であるが)変数の値を入れ替える関数 swap()を考えてみる。
以下のサンプルコードswap-test.cのように値を交換する関数swap()を定義しても、呼び出し側の変数u,vの値は書き換わらず、値が交換されない。
#include <stdio.h>
/* 2個の変数の値を入れ替える関数 (意図したとおりには動作しない例) */
void
swap(int a, int b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
int main(void)
{
int u, v;
u = 2; v = 3;
printf("u=%d v=%d\n", u, v);
swap(u, v);
printf("u=%d v=%d\n", u, v);
return 0;
}
このコードの実行結果は以下のようになる。関数swap()を呼び出しても値の交換がされていないことが分かる。
u=2 v=3
u=2 v=3
以下の図は上記の例で値が交換されない理由を示したものである。
main関数内の変数 u, vの値は関数 swap()を呼び出す際にそれらの値が変数 a, bにコピーされる。関数swap内ではコピーされた値同士を交換するため元の変数 u, vの値は不変である。
ポインタを使うとこの問題が解決できる。
関数の引数としてポインタ変数を渡す場合、ポインタ変数を介して値を書き換えると、ポインタ変数が指していた変数の値も書き換わるため、これを利用してC言語では参照渡し (call by reference)(関数の中で変数の値を書き換えると呼び出し側の変数も書き換わる)を実現することが出来る。
先のコードを以下のように引数をポインタ変数で受け渡すようにすると、関数swap()を呼び出すと呼び出し側の変数u,vの値が入れ替わる。
(引数がポインタ変数であるため、関数swap()を呼び出す際に変数のアドレスを渡す必要がある)
#include <stdio.h>
/* 2個の変数の値を入れ替える関数 (意図したとおりには動作する例) */
void swap(int *a, int *b) /* 引数がポインタ変数 */
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int u, v;
u = 2; v = 3;
printf("u=%d v=%d\n", u, v);
swap(&u, &v); /* 変数のアドレスを渡す */
printf("u=%d v=%d\n", u, v);
return 0;
}
このコードの実行結果は以下のようになる。関数swap()を呼び出すことで値の交換がされていることが分かる。
u=2 v=3
u=3 v=2
以下の図はポインタを使うことにより値が交換される理由を示したものである。
関数 swap()を呼び出す際に main関数内の変数 u, vのポインタ値(アドレス)が変数 a, bに格納される。関数swap内では変数 a, bに格納された変数 u, vのポインタ値(アドレス)を使って、変数 u, vの値を直接参照することができる。
補足:
C言語の拡張言語であるC++言語ではポインタを使うことなく、参照渡しが実現できる。
上記関数swap()の場合は以下のように、引数名の前に"&"をつけることで、関数の中で行なった値の変更が呼び出し側の変数の値にも反映される。
void swap(int &a, int &b) /* 参照渡し */
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
swap-test.cをコンパイル・実行し、swap関数によって値が交換されないことを確認してください。
また、関数の引数をポインタに変更して参照渡しにすることで、値が正しく交換されるようにしてください。