配列を関数に渡す
配列は写しが渡らない
第6章で作った点数の配列を、関数に渡してみます。書き方はこうです。
int sum_scores(const int scores[], int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += scores[i];
}
return total;
}
int total = sum_scores(scores, 5); // & は付けない呼ぶときに & を付けないのは、前々回に見たとおり、配列名がすでに先頭要素の番地だからです。そして渡っているのも、配列全部の写しではなく 先頭の番地だけ です。要素が 1000 個あっても渡るのは番地 1 個なので、大きな配列でも呼び出しは軽く済みます。
引数の int scores[] は、実は int *scores と書いても完全に同じ意味になります。角カッコの書き方は「配列を受け取るつもりです」と読む人に伝えるための見た目で、コンパイラにとっては両方ともポインタです。
個数は自分で渡す
番地しか渡らないということは、関数の中では要素が何個あるか分からないということです。呼び出し元で sizeof(scores) / sizeof(scores[0]) が使えても、関数の中の scores はポインタなので同じ計算は成り立ちません。
だから配列を渡す関数は、必ず 要素数を第 2 引数で一緒に渡す 形にします。C の標準ライブラリも同じ作りになっています。個数を間違えると配列の外まで読み書きしてしまい、静かに壊れるので、渡す数には気をつけてください。
中で書き換えると呼び出し元も変わる
番地が渡っているので、関数の中で scores[i] を書き換えると、呼び出し元の配列がそのまま変わります。前回のポインタ引数と同じ仕組みです。
void add_bonus(int scores[], int n, int bonus) {
for (int i = 0; i < n; i++) {
scores[i] += bonus;
}
}書き換えるつもりがない関数には const int scores[] と書いておくと、うっかり代入したときにコンパイラが止めてくれます。読むだけの関数には付ける習慣にしておくと安全です。
まとめると、配列の受け渡しで押さえるのは 3 点です。呼ぶときに & は付けない、要素数は別の引数で渡す、関数の中の書き換えは呼び出し元に届く。この 3 つが分かっていれば、第6章で書いた成績管理の処理は、そのまま関数へ移せます。
では、点数の配列を関数に渡して、合計と加点をやってみましょう。
要件
- 5つの点数を読み込んで配列に入れる
- int sum_scores(const int scores[], int n) で合計を返す
- void add_bonus(int scores[], int n, int bonus) で全員に加点する
- 加点前の合計を 「合計は405点です」 の形で表示する
- 5点加点してから 「加点後の合計は430点です」 と 「加点後の平均は86.0点です」 を表示する
入出力例
main("80 95 70 60 100") → "合計は405点です
加点後の合計は430点です
加点後の平均は86.0点です"
main("60", "60", "60", "60", "60") → "合計は300点です
加点後の合計は325点です
加点後の平均は65.0点です"