ポインタと関数
ポインタの項ではポインタの概要を説明しましたが、この説明だけではポインタの有効な使い方はあまりイメージできないでしょう。
実際のところ、ポインタは「高速化」「効率化」のために使われることが多く、これらを無視すればポインタを使わなくてもそれなりにプログラムは作れます。
ポインタは扱い方が難しいので無理して使うことはありません。
しかし、やはりポインタを使うと便利な場面は存在します。
その代表例が「関数から複数の値を受け取りたい場合」です。
引数のポインタ渡し
まずは以下のサンプルコードをみてください。
#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型の整数値を実引数に指定した場合、その値のコピーが関数に渡されます。
コピーなので、関数内で引数を書き換えても呼び出し元の変数には一切影響しません。
ポインタ渡しの場合も、ポインタ変数の中身であるアドレス値のコピーが関数に渡されることになります。
コピーなので、アドレス値を書き換えても呼び出し元には影響しませんが、アドレスが指し示す値は関数内からも呼び出し元からも同じ場所を参照しています。
そのため、関数内からポインタを通してアドレスの先の値を書き換えると呼び出し元の変数の値も書き換わるのです。
C言語の引数のポインタ渡しは「参照渡し」と説明されることがあります。
しかしC言語には参照渡しの機能はなく、全て値渡しです。
つまりポインタ変数も通常の変数も、変数の中身の値をコピーして関数に渡しています。
(通常の変数の値は実データ、ポインタ変数の値はアドレス)
C++には、これとは異なる動作をする「参照」という機能があり、この参照を引数に利用する「参照渡し」があります。
どちらも「同じデータを"参照"する」ことはできますが、C++の「参照」はデータのコピーは発生せずオブジェクトそのものが渡されます。
機能としては似ていますが別物なので、混同しないように注意してください。