C++超入門⑦ ポインタと参照を理解しよう
メモリアドレス、ポインタ、参照、nullptrの意味を、短いコードと図に近い考え方で整理します。
第7回では、C++でつまずきやすいポインタと参照を扱います。
最初から複雑なメモリ管理をする必要はありません。まずは「変数そのものではなく、変数が置かれている場所を扱う仕組み」と理解しましょう。
この回でできるようになること
- メモリアドレスの意味を知る
- ポインタで変数の場所を保存する
- ポインタを通して値を読む・変更する
- ポインタと参照を使い分ける
変数には置き場所がある
プログラムを実行すると、変数はパソコンのメモリへ置かれます。
メモリは、番号が付いたたくさんのロッカーに似ています。変数の値が荷物、ロッカー番号がメモリアドレスです。
変数の前に**&**を付けると、その変数のアドレスを取得できます。
#include <iostream>
int main() {
int score = 80;
std::cout << score << '\n';
std::cout << &score << '\n';
return 0;
}
アドレスの表示は実行するたびに変わることがあります。数字そのものを覚える必要はありません。
ポインタにアドレスを保存する
ポインタは、メモリアドレスを保存する変数です。
int score = 80;
int* scorePointer = &score;
| 部分 | 意味 |
|---|---|
| int* | int型の変数を指すポインタ |
| scorePointer | ポインタの名前 |
| &score | scoreのアドレス |
scorePointerはscoreそのものではなく、scoreが置かれている場所を保存しています。
ポインタから値を読む
ポインタの前に*****を付けると、指している場所の値を読み書きできます。
#include <iostream>
int main() {
int score = 80;
int* scorePointer = &score;
std::cout << *scorePointer << '\n';
*scorePointer = 95;
std::cout << score << '\n';
return 0;
}
*scorePointerは「scorePointerが指している先の値」という意味です。
*scorePointerへ95を入れると、元のscoreも95へ変わります。同じ場所を見ているためです。
nullptr
どの変数も指していないポインタにはnullptrを入れます。
int* numberPointer = nullptr;
if (numberPointer != nullptr) {
std::cout << *numberPointer << '\n';
}
nullptrのポインタから値を読もうとすると、プログラムが停止する原因になります。値を読む前に、nullptrではないか確認します。
参照とは
参照は、すでにある変数へ別名を付ける仕組みです。
int score = 80;
int& scoreReference = score;
scoreReference = 90;
std::cout << score << '\n';
scoreReferenceはscoreの別名なので、scoreReferenceを変更するとscoreも変わります。
関数で参照を使う
参照を引数にすると、関数から元の変数を変更できます。
#include <iostream>
void addOne(int& number) {
number++;
}
int main() {
int count = 5;
addOne(count);
std::cout << count << '\n';
return 0;
}
通常のint numberでは値のコピーを受け取ります。int& numberでは、呼び出し元の変数そのものを参照します。
ポインタと参照の違い
| 比較 | ポインタ | 参照 |
|---|---|---|
| 保存するもの | 変数のアドレス | 変数の別名 |
| 何も指さない状態 | nullptrを使える | 基本的に作れない |
| 指す相手の変更 | できる | 作成後は変えない |
| 値へのアクセス | *を付ける | 普通の変数と同じ |
相手が存在しない可能性がある場合はポインタ、必ず存在する値を別名で扱う場合は参照が分かりやすいことがあります。
この段階では、newやdeleteを使った手動のメモリ管理は後回しで構いません。複数の値にはvector、所有関係が必要な場面ではスマートポインタという安全性を高める仕組みを学んでから使いましょう。
小さな練習
int型の変数hpへ100を入れ、そのアドレスをポインタへ保存してください。
ポインタを通してhpを80へ変更し、元のhpを表示して確認しましょう。
模範解答
#include <iostream>
int main() {
int hp = 100;
int* hpPointer = &hp;
*hpPointer = 80;
std::cout << hp << '\n';
return 0;
}
解説
&hpでhpのアドレスを取得し、hpPointerへ保存しています。*hpPointerは、そのアドレスに置かれた値を表します。そこへ80を代入するため、元のhpも80になります。
まとめ
- ポインタは、変数が置かれているアドレスを保存します。
- *を使うと、ポインタが指す先の値を読み書きできます。
- 参照は、すでにある変数の別名です。