ポインタの&と*
番地を入れておく変数
前回、&score で変数の番地を取り出せることを見ました。取り出した番地は、その場で使い捨てにする必要はありません。番地も 1 つの値なので、変数に入れて持ち歩けます。
番地を入れるための変数を ポインタ と呼びます。宣言は型名のうしろに * を付けます。
int score = 80;
int *p; // int の置き場所の番地を入れる変数
p = &score; // score の番地を入れるint *p; は「p は int を指すポインタです」と読みます。宣言と代入をまとめて int *p = &score; と書くこともできます。
なぜ型を書くのか
番地の入れ物なのに、どうして int と書く必要があるのでしょうか。前回の最後に触れたとおり、番地は先頭のロッカー番号でしかないからです。そこから何バイト読めばよいか、読んだビット列をどう解釈すればよいかは、番地だけでは決まりません。int * なら 4 バイトを整数として、double * なら 8 バイトを小数として読む、という指示が型に入っています。だから double の番地を int * に入れることはできません。
* で中身を読む
ポインタが持っている番地の先を見るには、* を付けます。これを 間接参照 と呼びます。
printf("%d\n", *p); // p が指している場所の値、つまり 80宣言のときの * と、使うときの * は見た目が同じで紛らわしいのですが、役割は別物です。宣言の * は「これはポインタです」という印、使うときの * は「指している先を見ろ」という命令です。
* を左に書くと書き換えになる
*p は読むだけでなく、代入の左辺にも置けます。
*p = 95;
printf("%d\n", score); // 95 になっているp を通して score そのものを書き換えました。score という名札を使わずに値を変えられた、という点がポインタの本質です。ここが次の 2 回、scanf の正体と、関数から呼び出し元を書き換える話につながります。
指す先を決める前に使わない
int *p; と宣言しただけのポインタには、何の番地が入っているか分かりません。前章までの初期化していない変数と同じで、ごみの値が入っています。その状態で *p を読み書きすると、まったく関係のない場所を触ることになり、プログラムはたいてい異常終了します。
どこも指していないことを示したいときは int *p = NULL; と書いておく習慣があります。NULL は「有効な番地ではない」と決められた特別な値なので、if (p != NULL) で使ってよいかを確かめられます。まずは、指す先を決める前に * を使わない、という 1 点だけ守ってください。
では、点数をポインタ経由で読み書きしてみましょう。
要件
- int score に 80 を入れる
- int *p に score の番地を入れる
- *p で読んだ値を 「pが指す値は80点です」 の形で表示する
- *p への代入で score を 95 に書き換え、score を 「書き換えたあとのscoreは95点です」 の形で表示する
入出力例
main("") → "scoreの値は80点です
pが指す値は80点です
書き換えたあとのscoreは95点です"