はじめに
前回の記事では、void func(int arr[])
という形で、関数に配列を渡す方法を学びました。その際、「配列を渡す際、実際には配列の先頭アドレスが渡される」と解説しました。
では、もっと直接的に、void func(int* p)
のように「ポインタ」を引数として宣言した場合はどうなるのでしょうか? arr[]
と*p
、この2つの書き方に違いはあるのでしょうか。今回は、この疑問を解き明かしながら、ポインタを引数として活用する方法を解説します。
結論:関数引数において int arr[]
と int* arr
は同じ意味
いきなり結論からお伝えすると、関数の引数として書く場合、int arr[]
とint* arr
は、コンパイラにとっては全く同じ意味として解釈されます。
どちらの書き方でも、関数は「int
型を指すポインタ」を受け取る、という点では同じです。
int arr[]
: 「配列が渡されることを期待している」という開発者の意図を伝えやすい、より可読性を意識した書き方。int* p
: 「ポインタ(アドレス)を受け取る」という、より仕組みに忠実な書き方。
どちらを使うかはコーディングスタイルによりますが、どちらも根本的にはポインタを扱っている、ということを理解するのが重要です。
ポインタ渡しでも「配列のサイズ」は失われる
int arr[]
とint* p
が同じ意味ということは、残念ながら、前回の記事で解説した「配列のサイズ情報が失われる」という弱点も共通しています。
ポインタはあくまで「ある一点のアドレス」を指しているに過ぎず、その先にデータが何個続いているかまでは分かりません。そのため、ご提示のコードのように、関数内でループ回数を5
のような固定値にしてしまうと、その関数は特定のサイズの配列でしか使えない、汎用性の低いものになってしまいます。
実践コード:ポインタ記法で平均値を求める
解決策は前回と全く同じです。「配列のサイズも、別の引数として一緒に渡す」。
ここでは、関数をdouble calculate_average(int* p_data, int size)
とポインタ記法で宣言し、内部ではポインタ演算を使って平均値を計算する、より実践的なコードを見ていきましょう。
C++コード例
#include <iostream>
using namespace std;
// 関数のプロトタイプ宣言(引数をポインタ記法で記述)
double calculate_average(int* p_data, int size);
int main()
{
const int NUM_ITEMS = 4;
int sales_data[NUM_ITEMS] = {150, 220, 180, 300};
cout << NUM_ITEMS << "日分の売上データが記録されています。\n";
// 関数を呼び出す方法は、引数が arr[] でも *p でも全く同じ
double average_sales = calculate_average(sales_data, NUM_ITEMS);
cout << "\n平均売上は " << average_sales << " です。\n";
return 0;
}
// 関数の定義
double calculate_average(int* p_data, int size)
{
double sum = 0;
// 受け取ったサイズ情報を使ってループ
for (int i = 0; i < size; i++) {
// ポインタ演算で各要素の値にアクセス
sum += *(p_data + i);
}
if (size == 0) {
return 0;
}
return sum / size;
}
コードの解説
double calculate_average(int* p_data, int size)
関数の引数を、ポインタp_data
と要素数size
として定義しています。calculate_average(sales_data, NUM_ITEMS)
関数を呼び出す側は、引数がint arr[]
だろうとint* p
だろうと、何も変わりません。配列の名前(=先頭アドレス)と、その要素数を渡すだけです。sum += *(p_data + i);
関数内部では、ポインタ演算*(p_data + i)
を使って、配列の各要素にアクセスしています。これはp_data[i]
と書いても全く同じ意味になりますが、ここではポインタを引数にしたことを明確にするため、あえてポインタ演算の形で記述しています。
まとめ
今回は、関数にポインタを渡す方法と、それが配列を渡すことと本質的に同じであることを解説しました。
- 関数の引数において、
int arr[]
とint* p
はコンパイラにとって同じ意味。どちらもint
型へのポインタを受け取る。 - 書き方の違いは、開発者の意図をどう表現するかのスタイルの違い。
- どちらの記法を使っても、関数側では配列のサイズが分からないという事実は変わらない。
- 解決策はただ一つ。「配列(ポインタ)と一緒に、その要素数を必ず渡す」こと。
この原則を徹底することで、C++のポインタや配列を引数に持つ関数を、安全かつ再利用性の高い形で設計することができます。