C言語入門:関数へのポインタ渡しと値渡し

ここでは、ポインタに関して理解するために、int型ポインタを関数に渡した場合の挙動を紹介します。

サンプル


#include <stdio.h>

void
funcA(int i)
{
  i = 7;
}

void
funcB(int *i)
{
  (*i) = 222;
}

int
main()
{
  int a = 1;

  printf("a=%d\n", a);

  funcA(a);
  printf("after funcA() : a=%d\n", a);

  funcB(&a);
  printf("after funcB() : a=%d\n", a);

  return 0;
}

上記例を実行すると「1」以下のように表示されます。


> ./a.out 
a=1
after funcA() : a=1
after funcB() : a=222

この結果を見ると、funcA()の中で変数の値を変更しても、main()の中のint aは変わらず、funcB()の中で(*i)を変更するとmain()の中のint aが変わることがわかります。

funcA()とfuncB()の違いは、変数の渡し方です。 funcA()は、intをそのまま「値渡し」しています。 一方、funcB()は、intのポインタを引数とする事で「参照渡し」をしています。

funcA()の引数の渡し方では、main()の中のint aは、funcA()の引数であるint iにコピーされて渡されます。 funcA()のint iには、main()のint aと全く同じ値が入っていますが、所詮はコピーでしかないので、その値を変更してもmain()のint aの値は変わりません。

一方、funcB()は、int aへのポインタをint *iとして渡しています。 このため、int *iは、int aの実体がある場所を指しています。 funcB()中で使われている(*i)というのは、「*iが指す実体に対して」という意味があるので、(*i)を変更すると、main()のint aの中身が変更されます。

もう一つ最後に少しややこしい話をしますが、funcB()のint *iという引数は参照渡しではありますが、実はfuncA()と同じでコピーでもあります。 何故コピーかというと、main()でfuncB()を呼び出すときに渡している引数であるint aへのポインタという「値」をint *iとしてコピーして渡しているからです。 たとえば、funcB()内で(*i)に対して何かの操作をするのではなく、iに対して操作をしてもint &aというmain()の中での表現に影響は与えません(ただしポインタであるiに対する変な操作を下後で値を代入したりするとバッファオーバーフローを発生させる可能性があるのでご注意下さい)。

最後に

関数の引数を扱うとき、値渡しとポインタ渡し(参照渡し)で意味が全く違ってきます。 値渡しで関数にデータを渡しておいて「あれ?変化しないぞ?」というバグで悩むのは、初心者が必ず通る道である気がしています。


次:__LINE__

- C言語入門 記事一覧

おまけ

IPv6基礎検定

YouTubeチャンネルやってます!