C++: ポインタ演算
最終更新:2026-08-31
レッスン23ではポインタ演算の基本(
p + n、p - q)を学びました。でもポインタにはまだ重要な内容があります —— ポインタの比較は?
void*ポインタは?ポインタのポインタは?
1. ポインタの比較
(1) 1.1 ポインタの比較は可能?
はい! ただし配列内のポインタ同士のみ意味を持ちます。
▶ サンプル 2:基本的なプログラミング練習(難易度 ⭐)
CPP
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* p1 = &arr[0];
int* p2 = &arr[3];
if (p1 < p2) { // ✅ p1はp2より前
std::cout << "p1はp2より前" << std::endl;
}
if (p2 - p1 == 3) { // ✅ ポインタの差は3
std::cout << "p1とp2の間は3要素" << std::endl;
}
return 0;
}
💡 ヒント: ポインタの比較はアドレスの比較(配列の順序)です。
(2) 1.2 ポインタの等価比較
CPP
#include <iostream>
int main() {
int x = 5;
int* p1 = &x;
int* p2 = &x;
if (p1 == p2) { // ✅ 同じポインタ
std::cout << "p1とp2は同じ" << std::endl;
}
int y = 10;
int* p3 = &x;
int* p4 = &y;
if (p3 != p4) { // ✅ 異なるポインタ
std::cout << "p3とp4は異なる" << std::endl;
}
return 0;
}
2. void* ポインタ
(1) 2.1 void* ポインタとは?
void* は汎用ポインタ —— 型を持たないアドレスを格納できます。
CPP
#include <iostream>
int main() {
int x = 5;
double y = 3.14;
char c = 'A';
void* p1 = &x; // ✅ void* はintを指せる
void* p2 = &y; // ✅ void* はdoubleを指せる
void* p3 = &c; // ✅ void* はcharを指せる
return 0;
}
💡 ヒント:
void*は間接参照できない(コンパイラが何バイト読むかわからない)void*はポインタ演算できない
(2) 2.2 void* の使い道
使い道:汎用関数を書く(例:std::memcpy は void* を使う)
CPP
#include <iostream>
#include <cstring>
int main() {
int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5];
// std::memcpy は void* で任意の型のポインタを受け取る
std::memcpy(arr2, arr1, 5 * sizeof(int));
for (int i = 0; i < 5; i++) {
std::cout << arr2[i] << " ";
}
std::cout << std::endl;
return 0;
}
他の使い道:動的メモリ確保(C言語の malloc は void* を返す)
(3) 2.3 void* から値を取り出す
void* から値を取り出すには、型付きポインタにキャストします。
CPP
#include <iostream>
int main() {
int x = 5;
void* p = &x; // void* にintのアドレスを格納
// ❌ 間接参照できない:void* は型が不明
// std::cout << *p << std::endl;
// ✅ int* にキャスト
int* intPtr = static_cast<int*>(p);
std::cout << *intPtr << std::endl; // 出力: 5
return 0;
}
💡 ヒント:
static_cast は C++ の型キャスト演算子で、C言語の (int*)p より安全です。
3. ポインタのポインタ
(1) 3.1 ポインタのポインタとは?
ポインタのポインタはポインタのアドレスを格納するポインタです。
CPP
#include <iostream>
int main() {
int x = 5;
int* p = &x; // pはxを指す
int** pp = &p; // ppはpを指す(ポインタのポインタ)
std::cout << "x = " << x << std::endl;
std::cout << "*p = " << *p << std::endl; // xと同じ
std::cout << "**pp = " << **pp << std::endl; // xと同じ
return 0;
}
出力:
TEXT 📖 参照専用x = 5 *p = 5 **pp = 5
(2) 3.2 ポインタのポインタの宣言
| 宣言 | 意味 |
|---|---|
int x; |
x は int 型 |
int* p; |
p は int* 型(intへのポインタ) |
int** pp; |
pp は int** 型(int*へのポインタ) |
int*** ppp; |
ppp は int*** 型(int**へのポインタ) |
💡 ヒント: 右から左へ読むとわかりやすいです:
int* p;→pis a pointer tointint** pp;→ppis a pointer to pointer toint
(3) 3.3 ポインタのポインタの使い道
使い道1:動的2次元配列
CPP
// 3×4の行列
int** matrix = new int*[3]; // matrixはポインタのポインタ
for (int i = 0; i < 3; i++) {
matrix[i] = new int[4];
}
使い道2:関数でポインタ自体を変更(ポインタを渡して変更)
CPP
#include <iostream>
void allocateInt(int** ptr) {
*ptr = new int(5); // *ptr(元のポインタ)を変更
}
int main() {
int* p = nullptr;
allocateInt(&p); // pのアドレスを渡す
std::cout << *p << std::endl; // 出力: 5
delete p; // 解放
return 0;
}
4. ポインタと const の関係
(1) 4.1 まとめ
| 宣言 | 名前 | ポインタの変更 | 指す値の変更 |
|---|---|---|---|
int* p; |
通常のポインタ | ✅ 可能 | ✅ 可能 |
const int* p; |
const int へのポインタ | ✅ 可能 | ❌ 不可 |
int* const p = &x; |
const ポインタ(定数ポインタ) | ❌ 不可 | ✅ 可能 |
const int* const p = &x; |
const への const ポインタ | ❌ 不可 | ❌ 不可 |
💡 ヒント: 右から左へ読む:
const int* p;→pis a pointer toconst intint* const p = &x;→pis aconstpointer toint
(2) 4.2 例:const int*(読み取り専用)
CPP
#include <iostream>
void printArray(const int* arr, int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// ❌ 修正不可:arr[i] = 0; // コンパイルエラー
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5);
return 0;
}
💡 ヒント:
const int* arr は読み取り専用関数の引数型として推奨 —— 「読むだけ、修正しない」を明示。
5. 実践例:ポインタで文字列関数を実装
▶ サンプル 1:自作 strcpy(難易度 ⭐⭐)
CPP
#include <iostream>
void myStrcpy(char* dest, const char* src) {
while (*src != '\0') {
*dest = *src; // 文字をコピー
dest++; // 次へ進む
src++;
}
*dest = '\0'; // 終端文字を追加
}
int main() {
char src[] = "Hello";
char dest[20];
myStrcpy(dest, src);
std::cout << "src = " << src << std::endl;
std::cout << "dest = " << dest << std::endl;
return 0;
}
出力:
TEXT 📖 参照専用src = Hello dest = Hello
💡 ヒント: このプログラムはポインタと参照渡しの両方を使い、ポインタの強力さを理解できます。
▶ サンプル 3:ポインタの比較(難易度 ⭐⭐)
CPP
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* start = arr;
int* end = arr + 5;
// ポインタ比較で反復
int* p = start;
while (p < end) {
std::cout << *p << " ";
p++;
}
std::cout << std::endl;
// ポインタ減算で位置を求める
int* mid = arr + 2;
int index = mid - arr;
std::cout << "中央要素のインデックス: " << index << std::endl;
return 0;
}
出力:
TEXT 📖 参照専用10 20 30 40 50 中央要素のインデックス: 2
❓ よくある質問
Q ポインタのポインタはいつ使う?
A 実務では以下のような場面で使います:
- 動的2次元配列(
new int*[3]) - 関数でポインタ自体を変更(
void foo(int** p)でpを変更)
それ以上のポインタ(int*** など)はめったに使わない
📖 まとめ
- ポインタの比較は可能(配列内のみ)
void*は汎用ポインタ、間接参照不可、ポインタ演算不可- ポインタのポインタはポインタのアドレスを格納(
int**) - ポインタ + const は読み取り専用、推奨は
const int*(読み取り専用) - ポインタで文字列関数を実装し、ポインタの強力さを理解
📝 練習問題
-
初級(難易度 ⭐):
int変数x = 5、int*ポインタpでxを指し、int**ポインタppでpを指してください。x、*p、**ppを出力して確認。 -
中級(難易度 ⭐⭐): 関数
void swap(int** a, int** b)を書いてください。ポインタ自体を交換。TEXT 📖 参照専用交換前:p1はxを指し、p2はyを指す swap(&p1, &p2)後:p1はyを指し、p2はxを指す -
上級(難易度 ⭐⭐⭐):
void*とポインタ演算を使って、自作myMemcpy関数を実装:- 関数シグネチャ:
void myMemcpy(void* dest, const void* src, size_t n) - 機能:
srcからnバイトをdestにコピー - ヒント:バイト単位でコピー(
char*ポインタを使う)
- 関数シグネチャ:
6. 🚀 次のステップ
ポインタの応用を理解したら、次は 参照(Reference)(レッスン25) —— ポインタの「安全版」、C++独自の機能を学ぼう!