本文へスキップ
BecomeCoder
ブログ一覧コース一覧

C言語のポインタがわからない人へ|住所と中身の違いを動かして理解する

#C言語#ポインタ#初心者#C++

結論:ポインタは「値そのもの」ではなく「値が置いてある場所(アドレス)を覚えておく変数」です。& が「住所を調べる」、* が「その住所へ行って中身を見る/書き換える」という2つの動作だと分けて覚えれば、迷わなくなります。

なぜポインタは「わからない」「難しい」と感じるのか

「C言語 ポインタ わからない」「ポインタ 難しい」で検索する人のほとんどは、int *p = &x; のような1行で *& が入り混じって出てくることに混乱しています。しかも * は「宣言のときはポインタ型を作る記号」「使うときは中身を取り出す記号」と、同じ記号が場面によって意味を変えるので、余計にややこしく感じます。

理由ははっきりしています。ポインタは文章で説明されても像を結びにくく、実際に printf でアドレスと中身の両方を表示してみて初めて「ああ、そういうことか」と腑に落ちるタイプの概念だからです。このサイトの C言語コース は、ポインタの章までブラウザ上でそのままコードを実行できます(無料・登録不要・環境構築なし)。以下の説明を読んだら、すぐ実際のレッスンで手を動かしてみてください。

住所で理解する ― &(アドレスを調べる)と *(中身を見る)

一番のつまずきポイントは「変数」と「ポインタ」の関係です。次の例えで整理できます。

コードにするとこうなります。

int x = 10;
int *p = &x;   // pにはxの「住所」が入る(xの値そのものではない)

printf("%d\n", x);   // 10(部屋の中身を直接見る)
printf("%d\n", *p);  // 10(メモの住所へ行って中身を見る=結局同じ部屋)

*p = 20;              // メモの住所へ行って中身を書き換える
printf("%d\n", x);    // 20(xの中身が変わっている)

p に入っているのは xコピーではなく住所そのものなので、*p 経由で書き換えると元の x にも反映されます。ここが「値渡し」と根本的に違う点です。

この住所と中身の関係をそのまま動かして確認できるのが、メモリのしくみ ― 変数はどこにある?ポインタ入門 ― 住所(&)と中身(*) です。どちらもブラウザでそのまま printf("%p", &x) を実行して、実際のアドレスが表示されるところまで体験できます。

ポインタと配列がつながっている理由

ポインタの次のつまずきどころが「配列名は実はポインタっぽく振る舞う」という話です。C言語では配列名を関数に渡すと、配列そのものではなく先頭要素のアドレスが渡されます。だから arr[i]*(arr + i) は同じ意味になり、配列を関数に渡すときは「サイズも一緒に渡す」必要があります(配列自体は住所しか伝わらないため、大きさの情報が失われるからです)。

この仕組みは ポインタと配列 ― 密接な関係 で、実際に arr&arr[0] が同じアドレスを指すことを確認しながら学べます。関数に配列を渡すときのサイズの話は 配列を関数に渡す ― 大きさも一緒に渡す が前提知識として先にあると理解が速いです。

「値を書き換えてもらう」にポインタが要る理由

もう一つの代表的なつまずきが「関数に変数を渡しても、関数の中で書き換えた値が呼び出し元に反映されない」という現象です。これはC言語の関数呼び出しが基本的に**値渡し(コピーを渡す)**だからで、関数の中の変数は呼び出し元とは別物の部屋になっています。

呼び出し元の変数を書き換えてもらいたいときは、変数そのものではなくその変数の住所(アドレス)を渡すことで解決します。関数側は受け取った住所へポインタ経由でアクセスし、* を使って直接書き換えます。この一連の流れを実際に書いて確認できるのが ポインタと関数 ― 値を書き換えてもらう です。scanf("%d", &x)& が必要な理由も、ここまで理解すると自然に納得できます。

アドレスと参照の違い ― C++と比較するとすっきりする

「アドレス と 参照 違い」で調べている人向けに整理すると、C言語のポインタと、C++にある**参照(reference)**は近い役割を持ちつつ性質が違います。

つまり参照は「ポインタの中でも、常に有効で書き換え不可な使い方だけに絞って安全にしたもの」とイメージすると理解しやすいです。C++の ポインタ入門 ― 住所と中身 でC言語と同じ &/* の基本を確認したあと、参照 ― 変数に別名をつける参照渡し ― 関数に本体を渡す を実行すると、同じ「値を書き換えてもらう」目的をポインタと参照それぞれでどう書くか、両方をブラウザで見比べられます。

さらに先へ ― ポインタのポインタと関数ポインタ

基本が掴めたら、次に出てくる応用が「ポインタのポインタ(**)」と「関数ポインタ」です。

つまずきやすいところ ― Segmentation fault と double free

ポインタを使い始めると必ず一度は遭遇するのが、実行時に落ちるエラーです。

ここで慌てて丸暗記する必要はありません。エラーメッセージが出たら、まず該当ページで「何が起きているか」を確認する、という使い方で十分です。

次に読む

← ブログ一覧に戻る