10.ポインタ

10-4. ポインタと関数

◇ポインタと関数

いままで関数を使っていて変数の受け渡しで不便だと感じた事はありますか?
例えば、関数は引数として変数の値を代入するとき複数の値を代入する事ができますが値を返す時は1種類だけになります。

それは関数の戻り値が呼出し側では関数の値になるからです。

main()
{
    var = func(10,5);
}

int func(int x, int y)
{
    return (x + y);
}

つまり上の例の場合func(10)は15となりvarに代入されるのです。
returnに記述できる値は式でもかまいませんが1種類になります。

もし return x,y; とした場合どうなるか。
この時のカンマは順次演算子となり値は一番右側だけ評価されます。つまり
return y; と同じになってしまうわけです。

それでは2つの変数を代入してそれぞれ10倍にして返したい。
このように2つ以上の結果を得たい場合、どのようにすれば良いでしょう。

一つはグローバル変数を使うことです。

int x,y;

main()
{
    func(10,15);
}

void func(int a, int b)
{
    x = a * 10;
    y = b * 10;
}

こうすれば戻り値としての扱いではなくfunc関数内で代入処理をすることで解決します。
しかし、この方法ではローカル変数ではスコープ外となって代入操作ができないので不便です。
もちろん全てグローバル変数にすれば解決しますが、識別子名が重なったり間違って使用したりする可能性が大きくなりバグの原因となります。
またそれぞれの変数がどこで使われるのかが把握できなくなり困ります。

そういうことで限定的にスコープの枠を超えてアクセスが可能ならば非常に便利になるわけです。

それではさっきのプログラムをポインターを利用して書き換えてみます。
もちろん、変数はmainのローカル変数にします。

#include<stdio.h>
void func(int*, int*);

main()
{
    int x = 10, y = 15;
    func(&x, &y);
    printf("x=%d\ny=%d",x,y);
}

void func(int *a, int *b)
{
    *a = *a * 10;
    *b = *b * 10;
}

ここではprintfも加えてみました。
ここはとても重要な所なので一行ずつ解説していきます。

1.void func(int*, int*);

プロトタイプ宣言です。funcではポインタを使って間接参照をするので仮引数はポインタとして宣言する必要があります。

2.func(&x, &y);

こちらは呼出しの時の記述です。ポインタにはアドレスをセットするので引数にはポインタによって参照される変数のアドレスを入れます。

3.void func(int *a, int *b)

ここがfunc関数の本体です。この時点で

a = &x; b = &y;

の操作が行われています。ちなみに a と b はポインタです。
ここでもう一度忠告しますが、

int *p;

と宣言した時、

int * までが型で p が識別子名です。これを間違うとどうにもならないのでしっかりおぼえてください。

4.*a = *a * 10;
*b = *b * 10;

さいごに処理部分です。すでにアドレスのセットは終わっているのでそれぞれ

x = y * 10;
x = y * 10;

を意味します。