ポインタと関数

ポインタの活用例

ポインタの項ではポインタの概要を説明しましたが、この説明だけではポインタの有効な使い方はあまりイメージできないでしょう。

実際のところ、ポインタは「高速化」「効率化」のために使われることが多く、これらを無視すればポインタを使わなくてもそれなりにプログラムは作れます。
ポインタは扱い方が難しいので無理して使うことはありません。

しかし、やはりポインタを使うと便利な場面は存在します。
その代表例が「関数から複数の値を受け取りたい場合」です。

引数のポインタ渡し


#include <stdio.h>

//配列から最大値と最小値を得る
//成功した場合は1、失敗した場合は0を返す
int GetMinMax(const int arr[], size_t length, int* min, int* max)
{
    if (length == 0) {
        *min = *max = 0;
        return 0;
    }

    int tmpMin = arr[0];
    int tmpMax = arr[0];

    for (int i = 1; i < length; i++)
    {
        if (tmpMin > arr[i])
            tmpMin = arr[i];
        if (tmpMax < arr[i])
            tmpMax = arr[i];
    }

    *min = tmpMin;
    *max = tmpMax;

    return 1;
}

int main()
{    
    int numbers[] = { 12, 5, -9, 20, 4 };
    int minimum = 0;
    int maximum = 0;

    GetMinMax(
        numbers,
        sizeof(numbers) / sizeof(numbers[0]),
        &minimum,
        &maximum);

    printf("最小: %d、最大: %d", minimum, maximum);

    getchar();
}
最小: -9、最大: 20

自作関数GetMinMaxは、配列から最低値と最大値を取り出す関数です。

関数の戻り値はひとつしか指定できません。
関数の処理結果をふたつ以上受け取りたい場合は、変数のポインタを実引数で渡し、関数内で書き換えるという方法があります。
(他にも方法はあります)

この関数では、仮引数の*min*maxがポインタです。
引数名の前に間接演算子を書くと、ポインタを受け取ることを意味します。

関数の呼び出し側では、関数の処理結果を受け取るための変数をまず宣言します。
(32、33行目)
そして、それらの変数のアドレスをアドレス演算子(&)によって取り出し、GetMinMax関数の実引数に指定しています。

関数GetMinMaxは、8行目、および23、24行目でアドレス先の値を書き換えています。
変数minimumのアドレスと、仮引数minとして受け取ったアドレスは同一ですから、ポインタを通して値を書きかえると呼び出し元の変数の値も書き換えられます。
変数maximumと仮引数maxも同様です。

関数の戻り値は成功すれば1を返し、配列の要素数が0の場合は0を返します。
今回は必要がないので戻り値は利用していませんが、引数を通して処理結果を受け取るようにすれば戻り値はエラー判定に使用することができます。

引数の書き換えについて

C言語では、ポインタ渡しでも、通常の(ポインタでない)渡し方でも、関数の仮引数には常に「実引数をコピーしたもの」が渡されます。

引数のポインタ渡しのイメージ

例えばint型の整数値を実引数に指定した場合、その値のコピーが関数に渡されます。
コピーなので、関数内で引数を書き換えても呼び出し元の変数には一切影響しません。

ポインタ渡しの場合も、ポインタ変数の中身であるアドレス値のコピーが関数に渡されることになります。
コピーなので、アドレス値を書き換えても呼び出し元には影響しませんが、アドレスが指し示す値関数内からも呼び出し元からも同じ場所を参照しています。
そのため、関数内からポインタを通してアドレスの先の値を書き換えると呼び出し元の変数の値も書き換わるのです。