C++: C++ ポインタ
最終更新:2026-08-31
これまで、変数はメモリ上の「箱」のようなものだと学びました。
でも、その「箱」がメモリ上のどこにあるか知りたい場合は?
ポインタは変数のアドレスを格納する特別な変数 —— メモリの場所を指し示します。
1. ポインタとは?
(1) 1.1 ポインタの役割
| 役割 | プログラムでの用途 |
|---|---|
| 変数のアドレスを格納("どこにあるか") | ポインタ(アドレス) |
| アドレス先の値を取得("何が入っているか") | 変数(アドレス先のメモリ) |
ポインタの定義: 変数のアドレスを格納する変数。
(2) 1.2 なぜポインタが必要?
- 動的メモリ確保(
new/delete) - 関数で変数を変更(ポインタを経由)
- 配列の操作(配列とポインタの関係)
- データ構造の実装(連結リスト、ツリー、グラフ)
2. ポインタの宣言と初期化
(1) 2.1 ポインタの宣言
構文:
▶ サンプル 2:コード例(難易度 ⭐)
TEXT
📖 参照専用
型* ポインタ名;
例:
CPP
#include <iostream>
int main() {
int* pInt; // intへのポインタ
double* pDouble; // doubleへのポインタ
char* pChar; // charへのポインタ
return 0;
}
💡 ヒント:
型* は「型へのポインタ」を表します。
(2) 2.2 アドレス取得演算子(&)
& は変数のアドレスを取得します。
CPP
#include <iostream>
int main() {
int x = 5;
std::cout << "x の値:" << x << std::endl;
std::cout << "x のアドレス:" << &x << std::endl; // 例:0x7ffd4a3b
return 0;
}
出力例:
TEXT 📖 参照専用x の値:5 x のアドレス:0x7ffd4a3b
💡 ヒント: アドレスはシステムやコンパイラによって異なります。
(3) 2.3 ポインタの初期化
CPP
#include <iostream>
int main() {
int x = 5;
int* p = &x; // pはxを指す(pの中身はxのアドレス)
std::cout << "x のアドレス:" << &x << std::endl;
std::cout << "p の値:" << p << std::endl; // &x と同じ
return 0;
}
3. 間接参照(Dereference)
(1) 3.1 間接参照演算子(*)
* はポインタが指す値を取得します。
CPP
#include <iostream>
int main() {
int x = 5;
int* p = &x; // pはxを指す
std::cout << "x = " << x << std::endl; // 5
std::cout << "*p = " << *p << std::endl; // 5(間接参照)
return 0;
}
💡 ヒント:
*p は x と同じ値を返します!
(2) 3.2 ポインタで変数の値を変更
CPP
#include <iostream>
int main() {
int x = 5;
int* p = &x;
std::cout << "変更前:x = " << x << std::endl; // 5
*p = 100; // xの値を変更
std::cout << "変更後:x = " << x << std::endl; // 100
return 0;
}
4. ポインタのサイズ
(1) 4.1 ポインタの sizeof
CPP
#include <iostream>
int main() {
int* pInt;
double* pDouble;
char* pChar;
std::cout << "int* のサイズ:" << sizeof(pInt) << " バイト" << std::endl;
std::cout << "double* のサイズ:" << sizeof(pDouble) << " バイト" << std::endl;
std::cout << "char* のサイズ:" << sizeof(pChar) << " バイト" << std::endl;
return 0;
}
出力(64ビットシステム):
TEXT 📖 参照専用int* のサイズ:8 バイト double* のサイズ:8 バイト char* のサイズ:8 バイト
💡 ヒント: ポインタのサイズは型に関係なく同じ(64ビットシステムでは8バイト、32ビットでは4バイト) —— すべてアドレスを格納するからです。
5. ヌルポインタ(nullptr)
(1) 5.1 ヌルポインタとは?
ヌルポインタはどのオブジェクトも指さないポインタです。
CPP
#include <iostream>
int main() {
int* p = nullptr; // C++11以降の推奨書き方
if (p == nullptr) {
std::cout << "pはヌルポインタです" << std::endl;
}
return 0;
}
💡 ヒント: C++11より前は
NULL(または 0)を使っていましたが、現在は nullptr を使います。
(2) 5.2 ヌルポインタの間接参照
TEXT
📖 参照専用
#include <iostream>
int main() {
int* p = nullptr;
std::cout << *p << std::endl; // ❌ 未定義動作!
return 0;
}
実行結果:
TEXT 📖 参照専用Segmentation fault (core dumped) // LinuxTEXT 📖 参照専用Process finished with exit code -1073741819 // Windows
⚠️ ヌルポインタの間接参照は絶対にしないでください!
6. ポインタと関数
(1) 6.1 問題:関数で変数を変更できない
CPP
#include <iostream>
void modifyValue(int x) {
x = 100; // ❌ ローカルコピーを変更、元の変数は変わらない
}
int main() {
int a = 5;
modifyValue(a);
std::cout << "a = " << a << std::endl; // 5(変わらない)
return 0;
}
(2) 6.2 解決策:ポインタを使う
CPP
#include <iostream>
void modifyValue(int* p) {
*p = 100; // ✅ ポインタ経由で変更
}
int main() {
int a = 5;
modifyValue(&a); // アドレスを渡す
std::cout << "a = " << a << std::endl; // 100(変わった!)
return 0;
}
7. 実践例:変数の値を交換
▶ サンプル 1:ポインタでswapを実装(難易度 ⭐)
CPP
#include <iostream>
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 3, y = 5;
std::cout << "交換前:x = " << x << ", y = " << y << std::endl;
swap(&x, &y); // アドレスを渡す
std::cout << "交換後:x = " << x << ", y = " << y << std::endl;
return 0;
}
出力:
TEXT 📖 参照専用交換前:x = 3, y = 5 交換後:x = 5, y = 3
💡 ヒント: C++では参照を使う —— 参照でswapを実装するほうが簡潔です。
8. よくあるエラー
(1) 8.1 初期化していないポインタの間接参照
TEXT
📖 参照専用
#include <iostream>
int main() {
int* p; // ❌ 未初期化(ワイルドポインタ)
std::cout << *p << std::endl; // ❌ 未定義動作!
return 0;
}
解決:
nullptrで初期化するか、有効な変数のアドレスを代入してください。
(2) 8.2 ポインタの型不一致
CPP
#include <iostream>
int main() {
int x = 5;
double* p = &x; // ❌ エラー:int* と double* は互換性がない
return 0;
}
コンパイルエラー:
TEXT 📖 参照専用error: cannot convert 'int*' to 'double*' in initialization
▶ サンプル 3:ポインタで2つの数を交換(難易度 ⭐⭐)
CPP
#include <iostream>
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
std::cout << "交換前: x = " << x << ", y = " << y << std::endl;
swap(&x, &y);
std::cout << "交換後: x = " << x << ", y = " << y << std::endl;
return 0;
}
出力:
TEXT 📖 参照専用交換前: x = 10, y = 20 交換後: x = 20, y = 10
❓ よくある質問
Q ポインタと参照の違いは?
A 主な違い:
- ポインタは
nullptrになれるが、参照は必ず何かを指す - ポインタは変数を変更できるが、参照は初期化時に束縛
- ポインタは
*pで間接参照、参照は変数名をそのまま使う
推奨:
📖 まとめ
- ポインタは変数のアドレスを格納する特別な変数
- 宣言:
型* ポインタ名; - アドレス取得:
&変数名 - 間接参照:
*ポインタ名 - ヌルポインタ:
nullptr - ヌルポインタや未初期化ポインタの間接参照は危険
📝 練習問題
-
初級(難易度 ⭐):
int変数x = 10を作り、int*ポインタpでxを指してください。xの値と*pの値を出力してください。 -
中級(難易度 ⭐⭐): 関数
void addTen(int* p)を書いてください。ポインタが指す変数に10を加算します。mainでテストしてください。 -
上級(難易度 ⭐⭐⭐): プログラムを書いてください。3つの
int変数a, b, cと3つのポインタを作ります。ポインタ経由で変数の値を100に設定し、出力してください。
9. 🚀 次のステップ
ポインタの基本を理解したら、次は ポインタと配列(レッスン23) —— 配列とポインタの深い関係、ポインタ演算を学ぼう!