ポインタの復習
この章でやること
入門の第7章では、ポインタを「入口だけ」と断って終えました。& で番地を取り出し、* で指す先を読み書きし、関数に番地を渡すと呼び出し元を書き換えられる、というところまでです。この中級コースは、その先を全部扱います。ポインタを 1 つ進めると何が起きるのか、配列とポインタはなぜ混ざって見えるのか、必要な分だけメモリを自分で借りるとはどういうことか、という順に進みます。
最初の回は足場の確認です。新しい道具は出てきません。ただ、ここから先はポインタが常時登場するので、& と * を止まらずに読み書きできる状態にしておきます。
& は番地を取り出す
変数はメモリのどこかに置かれています。その置き場所の先頭番号を取り出すのが & です。
int tanaka = 80;
int *p = &tanaka; /* tanaka の番地を p に入れる */int *p は「p は int を指すポインタです」と読みます。ポインタが持っているのは番地であって、点数そのものではありません。
* は指す先を見る
*p と書くと、p が持っている番地の先を見ます。これを 間接参照 と呼びます。
printf("%d\n", *p); /* 80 */
*p = 85; /* tanaka が 85 になる */
printf("%d\n", tanaka);*p は読むだけの記号ではありません。代入の左辺にも置けます。tanaka という名札を一度も書かずに tanaka を書き換えられる、というのがポインタの本質でした。
宣言のときの * と、使うときの * は見た目が同じで別物です。宣言の * は「これはポインタです」という印、使うときの * は「指す先を見ろ」という命令です。
関数に番地を渡す
C の引数は値渡しだけです。点数をそのまま渡すと写しが届くので、関数の中で書き換えても呼び出し元には戻りません。番地を渡せば、届いた番地の先を触れるので、呼び出し元そのものを書き換えられます。
void add_bonus(int *score, int bonus) {
*score += bonus;
}*score += bonus; は「score が指している先に bonus を足す」です。score += bonus; と書くとポインタのほうを動かしてしまい、まったく別の意味になります。この違いは次回のポインタ演算そのものなので、いまは「* を忘れると意味が変わる」とだけ押さえておいてください。
どこも指していないポインタ
int *p; と宣言しただけのポインタには、ごみの番地が入っています。そのまま *p を触ると関係のない場所を壊すので、行き先が決まっていないうちは int *p = NULL; と書いておきます。NULL は「有効な番地ではない」と決められた値で、使う前に if (p != NULL) で確かめられます。この習慣は、動的メモリに入る第2章から本当に効いてきます。
では、2 人分の点数を読み込んで、ポインタ越しにボーナスを足してみましょう。
要件
- 田中さんの点数を指すポインタ p を作り、*p で最初の点数を表示する
- void add_bonus(int *score, int bonus) を完成させる
- add_bonus を使って 2 人に 5 点ずつ加える
- 「田中の点数は80点です」「鈴木の点数は85点です」の形で表示する
入出力例
main("80 85") → "ボーナス前の田中の点数は80点です
田中の点数は85点です
鈴木の点数は90点です"
main("72", "64") → "ボーナス前の田中の点数は72点です
田中の点数は77点です
鈴木の点数は69点です"
main("95 100") → "ボーナス前の田中の点数は95点です
田中の点数は100点です
鈴木の点数は105点です"