みえるCお試し版

トラック 11・ポインタ基礎

& と *——住所を書く・訪ねる

読む目安 約12分 ねらい: &で番地を取得し、*で指す先の値を読み出せる。pと*pの違いを説明できる

このレッスンの要点

  • &変数 は住所を調べる、*ポインタ はその住所を訪ねる(間接参照)
  • *&a は a に戻る——2つは逆向きの操作
  • p は番地、*p は指す先の値。まったくの別物
  • 矢印は絵の都合であり、実体は「番地という数値」

ポインタで使う記号は2つだけです。& と *。この2つの意味を取り違えないことが、最大の関門です。

& は「住所を調べる」

トラック10で使った演算子です。変数の前に付けると、その変数の番地が得られます。

int a = 5;
printf("%p\n", (void *)&a);   // a の番地が出る

日本語にすると「a はどこにいますか」。答えとして返ってくるのが番地です。

* は「その住所を訪ねる」

新しい道具です。番地の前に付けると、その番地の場所へ行って中身を取り出します。これを間接参照(デリファレンス)と呼びます。

int a = 5;
int *p = &a;
printf("%d\n", *p);   // 5 が出る

p の中身は番地ですが、*p と書いた瞬間に「その番地を訪ねた先にあるもの」、つまり a そのものになります。

ポインタ姫& で住所を調べ、* でその住所を訪ねる。郵便屋の仕事はこの2つだけですのよ

2つは打ち消し合う

& と * は逆向きの操作なので、続けて書くと元に戻ります。

int a = 5;
printf("%d\n", *&a);   // 5(a の住所を調べて、そこを訪ねた)

「a の住所を調べて、その住所を訪ねる」=「a」。当たり前ですね。この当たり前が腹に落ちていれば、ポインタの式は落ち着いて読めます。

動く様子を目で見る

宣言 → & で代入 → * で読む、という一番基本の流れをメモリの図で追ってみましょう。矢印が出た瞬間に注目してください。

ここにはメモリの図が入る。アプリの中では、1行ずつ動かして見られる。 アプリでこのレッスンを開く

p が作られたとき、中身は「?」でした。まだどこの番地も書いていない宛先メモです。つぎの行で &a の結果(図では 0x1000)が書き込まれ、はじめて矢印が引かれます。

ビット矢印って、線でつながってるみたいに見えるけど……

いい着眼点です。矢印は絵の都合で、実際に線がつながっているわけではありません。p というロッカーの中に「0x1000」という数値が書いてあるだけです。ポインタは矢印ではなく数値、これは後で誤解トップ5にも登場します。

表示のしかたを整理する

3つを並べて確認しておきましょう。

int a = 5;
int *p = &a;

printf("%d\n", a);              // 5      … 値そのもの
printf("%p\n", (void *)&a);     // 0x...  … a の番地
printf("%p\n", (void *)p);      // 0x...  … p の中身(=a の番地。同じ値)
printf("%d\n", *p);             // 5      … p の指す先の値

2行目と3行目が同じ番地になるのがポイントです。ここが一致していれば、p は正しく a を指しています。

まとめ

& は住所を調べる、* はその住所を訪ねる。*&a は a に戻ります。p と *p は別物で、前者は番地、後者は指している先の値です。この区別だけは、声に出して確かめる癖をつけてください。

アプリでこのレッスンを動かして学ぶ(無料・登録なし)

動かして確かめるコード

#include <stdio.h>

int main(void) {
    int a = 10;
    int *p = &a;
    *p = 25;
    printf("%d\n", a);
    return 0;
}
#include <stdio.h>

int main(void) {
    int a = 5;
    int *p = &a;
    printf("%d\n", *p);
    a = 7;
    printf("%d\n", *p);
    return 0;
}

確認問題

確認問題 1

p が a を指すようにします。空欄に入るのはどれでしょう。

int a = 5;
int *p = ____;
  1. &a
  2. a
  3. *a
  4. a*
答えを見る

正解: A(&a)

& は「住所を調べる」演算子なので、&a で a の番地が得られます。a と書くと値の 5 を入れようとすることになり、型が合わずコンパイルエラーです。*a は int には使えません(* を使えるのはポインタに対してだけです)。

確認問題 2

次のプログラムは何を出力するでしょう。

#include <stdio.h>

int main(void) {
    int a = 5;
    int *p = &a;
    printf("%d\n", *p);
    return 0;
}
  1. 5
  2. a の番地(0x7ffd... のような値)
  3. 0
  4. コンパイルエラー
答えを見る

正解: A(5)

*p は「p の持つ番地を訪ねた先の中身」なので 5 です。p そのもの(番地)を表示したいなら printf("%p\n", (void *)p); と書きます。p は番地、*p は値——この2つを声に出して区別する癖をつけましょう。

「ポインタ基礎」の目次

  1. ポインタとは「宛先メモ」
  2. & と *——住所を書く・訪ねる
  3. ポインタ変数の宣言と型有料版
  4. NULLポインタとnullptr(C23)有料版
  5. ポインタ経由の読み書き有料版
  6. 関数に変数を「参照渡し」する有料版
  7. ポインタと配列の深い関係有料版
  8. ポインタのよくある誤解トップ5有料版

この先へ ポインタ・メモリ・構造体・ファイル入出力まで、全26トラック・211レッスン・演習508問は有料版に入っている。

有料版(BOOTH)を見る

ほかのトラック

姉妹品: 手を動かして覚える Linux 教材 Linux とインフラの仕組みを地図で学ぶ

Web 版 / BOOTH