アドレス
変数、配列の値は、コンピューターのメモリ上に保存されている。
メモリは1バイトごとに区切られ「アドレス」という通し番号で管理されている。
●変数のアドレスを取得する方法
&(アンパーサント)を変数の前につけると「変数のアドレス」を指すことになる。
&変数名; ⇒ 変数の先頭アドレスを取得する記法
●配列のアドレスを取得する方法
配列名のみを記載すると「配列の先頭アドレス」を指すことになる。
int array[10](一次元配列)とした場合
①先頭アドレスを取得したいとき
int *p; ⇒int型ポインタ変数
p = array; ⇒配列名のみを記述すると、配列の先頭アドレスが取得可能。
②配列内の3つ目の要素を取得したいとき
p = $array[2]; ⇒&をつけることで配列要素のアドレスも取得可能。
ポインタ(ポインタ変数)
データの塊の先頭アドレスを格納しておく変数。
https://9cguide.appspot.com/15-01.html ←この章だけでも読んでおくべし。
(抜粋)
世間では、アドレスの値を記憶する変数をポインタと呼んでいますが、
これは本当はあまり正確な呼び方ではありません。
なぜなら、ポインタとは、アドレスを扱う機能3つの総称だからです。
ポインタという呼び方は総称であり、正確には3種類に別れています。
1つ目は、ポインタ型です。
今まで出てきたint型やdouble型と同じような型です。
ただし、ポインタ型の場合、それらとは少し異なる特徴があります。
2つ目は、ポインタ値です。
これは、ポインタ型で扱える数値、要するにアドレスのことです。
整数や実数といった数値の区別と同様に、ポインタ値という区別があるのです。
3つ目は、ポインタ変数です。
これは、ポインタ型で宣言された、ポインタ値を記憶できる変数のことです。
int型の変数やdouble型の変数と基本的には同じことです。
int a; // 変数宣言
a = 10; // 代入
int* p1; // ポインタ変数(アドレスが入る変数)
p1 = &a; // 変数aのアドレスをp1に代入(例:p1の中身は0x8000)
int b = *p1; // aのアドレスに格納されている値をbに代入(b = a と同じ)
b = a; // これは b = a; なので、普通のコピー
*p1 = 12; // aのアドレスにある値を12に書き換える(a = 12 と同じ)=「間接代入」
a = 12; // 当然、これも a = 12
「アドレスを格納する変数がポインタ、*
は中身を取り出す演算子」
→ この“二重構造”を理解しておくとポインタで迷わなくなる(by ChatGPT)
あなた:int p = &a; と int *p = &a; って意味が違うんですかね?そもそも*pって通常の変数とどう違うのか、って話になりますかね??
ChatGPT:
はい、これは C言語初学者が必ずと言っていいほどつまずくポイントであり、非常に重要な話です。
ここで一度、「*p
って何?」という根本からしっかり整理しましょう。
✅ int p = &a; と int *p = &a; の違い
① int p = &a;
これは**NG(ほぼ確実に警告またはエラー)**になります。
&a
は「aのアドレス」=整数値(アドレス値)だけど、p
は「int型」=普通の数値を格納する変数と宣言してる
👉 よって型が一致しない(ポインタじゃない変数にアドレスを代入しようとしてる)
② int *p = &a;
これはOK(正しい書き方)
p
は「int型の変数を指すポインタ」だと宣言されている&a
は「aのアドレス」なので、それを保持するのにふさわしい型
👉 アドレスを記憶して、その場所にある整数値を操作できる
✅ *p って何?どこで使われる?
*p
は「pが指しているメモリの中身を取り出す演算子」です。
たとえば:
cコピーする編集するint a = 10;
int* p = &a;
p
は「aのアドレス」=たとえば0x1000
を保持している*p
は「アドレス0x1000
にある値」=つまり10
💡これ、わかりやすく言うとこうです:
書き方 | 読み方 | 意味・中身 |
---|---|---|
p | ピー | アドレス(番地) |
*p | デリファレンス・ピー | その番地にある値 |
✅ 「*p は通常の変数と何が違うの?」
とても良い問いです。ポイントは:
項目 | a のような通常の変数 | *p のようなポインタ経由の変数 |
---|---|---|
正体 | メモリの中の値 | メモリの「中の中の値」 |
書き換え方法 | a = 20; | *p = 20; (ただし p が a を指している前提) |
使用例 | 単純な代入・計算など | 間接的なアクセス、関数の引数渡し、配列処理など |
✅ めっちゃざっくりまとめると:
cppコピーする編集するint* p; // ← p は「ポインタ」=住所を持つやつ
p = &a; // ← a の住所を p に渡す
*p = 20; // ← p が指してる先にある「a」に 20 を代入する
つまり、*p
は「ポインタを使って、最終的に値を操作する手段」です。
🔧 ゲーム開発での実用場面(導入トーク案)
🎮「ゲームではメモリの中を直接いじるような処理や、配列を動的に生成したり、キャラクターを大量に扱うとき、ポインタはむしろ“必須の道具”になります。C言語は“間接操作の言語”です。」
必要であれば、次は:
int** pp = &p;
の多重構造をクイズ形式で確認*p
の演算がどうアセンブリに変換されるか(例:mov eax, [ebp-4]
)
などのステップも補助できます。
このタイミングで「講義用クイズ」作りますか?
あなた:
int a; int *p; p = &a; ならわかりやすいですね。 int *p = &a; のように、宣言と代入を一緒にしようとすると、「え?pの内容って??」ってなっちゃう。
ChatGPT:
まさに、そこがポインタ初学者が混乱する最大のトラップなんです。
ゆきさんのように「宣言と代入を分けて理解する」視点は、教える側としてもめちゃくちゃ重要な指導方針になります。
✅ なぜ混乱するのか?:「*p = &a; に見える現象」
cコピーする編集するint* p = &a;
この書き方って、初心者にはこう見えがちなんです:
「
*p = &a;
ってこと?つまり*p
に&a
を代入してるの??」
…という誤読が起きます。
でも実際は:
int* p
は「pという名前のポインタ変数を宣言」していて、= &a;
はそのpに「aのアドレスを代入」してるだけ。
✅ 混乱を防ぐテク:宣言と代入は分けて教える!
これは完全に正しい指導法です。
cコピーする編集するint a;
int* p; // 「pはポインタです」とはっきり言える
p = &a; // 「aの住所をpに入れました」と段階的に見せられる
この方が、初心者にとって以下の点が明確になります:
p
がポインタであること&a
が「aの住所」であること*p
が「pが指す場所の中身」であること
✅ スライドや板書でやるなら…
次のように「見える形」にしてみるとわかりやすいです:
cssコピーする編集するa → [10]
&あ → 0x1000(例)
p → [0x1000]
*p → [10](つまり a の中身)
✅ 講義中トーク例:
💬「C言語は“宣言と代入を同時にできる”けど、それって実は2つの操作を1行でまとめてるだけです。いきなり書くと『どっちがポインタなの?中身はどこ?』って混乱しやすい。だから今は分けて書きます。」
✨ まとめると:
int *p = &a;
は正しいが、初学者には「誤読ポイント」int *p;
p = &a;
のように分けて教えるのが鉄板- その後、慣れてから短縮形に慣れさせればOK
ところで、ポインタのポインタって、**p2みたいな感じになるのでしょうか。 int* p1 = &a; int**p2 = &p1; こんな感じ?? int* p1 = &a; int*p2 = &p1; って書くと間違い??
ChatGPT:
はい、ゆきさんの理解はほぼ完璧に正しいです!
以下に明確に整理しておきます:
✅ 正しい多重ポインタの書き方
cコピーする編集するint a = 10;
int* p1 = &a; // p1 は a のアドレスを指す「ポインタ」
int** p2 = &p1; // p2 は p1 のアドレスを指す「ポインタのポインタ」
*p1
→a
の中身(10)*p2
→p1
の中身(aのアドレス)**p2
→a
の中身(10)
つまり:
変数名 | 意味 |
---|---|
p1 | a のアドレス(例:0x1000) |
p2 | p1 のアドレス(例:0x2000) |
*p2 | p1 の中身(=a のアドレス) |
**p2 | a の中身(10) |
❌ 間違った書き方(混乱ポイント)
cコピーする編集するint a = 10;
int* p1 = &a;
int* p2 = &p1; // ← ❌これは間違い
なぜ間違いか?
&p1
は「ポインタ変数p1
のアドレス」- それを受け取るには「ポインタのポインタ」でなければいけない
- なのに
int* p2
では「int型を指すポインタ」と宣言してしまっている
👉 要するに、ポインタの型と格納するアドレスの型が一致していない
💡型が“1段ずれる”感覚をつかむための表現
宣言 | 指す対象 |
---|---|
int | 整数 |
int* | 整数の住所 |
int** | ポインタの住所 |
つまり、int* p
は「int型の場所を指すポインタ」int** pp
は「int型の場所を指すポインタを指すポインタ」
🧠 トーク例(講義用)
💬「“ポインタのポインタ”って聞くと『何それ?』ってなるけど、実際は“住所の住所”を持ってるだけ。しかも、型が1段階ずれるだけで全部説明つくんです。
int
→int*
→int**
。型を見れば、何段目の住所を扱ってるのかがわかるんです。」
ご希望あれば、この「型のズレ」や「段数の意味」を視覚化したスライドや、多重ポインタ構造クイズも作成できます!
必要ですか?それとも次のトピックへ進めますか?
あなた:
教科書で多重ポインタに触れてないのですが、三重ポインタだとint*** p3っていうことですか??
ChatGPT:
はい、まさにそのとおりです!
三重ポインタは int*** p3;
と書きます。
C言語におけるポインタの段数は、アスタリスク(*
)の数で表現されます。
✅ 多重ポインタの構造まとめ
宣言 | 意味 |
---|---|
int a; | 整数(値) |
int* p1 = &a; | a のアドレスを記録するポインタ |
int** p2 = &p1; | p1 のアドレスを記録するポインタのポインタ |
int*** p3 = &p2; | p2 のアドレスを記録する三重ポインタ |
✅ 具体的な図(ざっくり)
makefileコピーする編集するa = 10 ← 普通の整数
p1 = &a ← a の住所を持つ
p2 = &p1 ← p1 の住所を持つ
p3 = &p2 ← p2 の住所を持つ
*p1 = 10 ← a の中身
**p2 = 10 ← p1 → a → 10
***p3 = 10 ← p2 → p1 → a → 10
✅ 教科書で出てこない理由
三重ポインタ以上は、使う状況がかなり限られているため、初学者向け教材では多くの場合カットされています。
ただし、実務では以下のようなケースで出てきます:
- 関数でポインタを返す関数のポインタ
- 多次元の構造体や配列の扱い
- 関数に「ポインタを受け取って、それを書き換える」ような場面
コメント