ポインタ(* / &)
『C++』では、変数のメモリアドレスを直接扱うポインタが言語の中核機能のひとつです。ポインタを使うと、メモリを効率よく操作したり、関数間で大きなデータを参照渡しにしたり、動的メモリ確保を行ったりできます。ここでは、ポインタの宣言から演算・nullptrの扱い・配列との関係まで、基本的な使い方を説明します。
構文
// ========================================
// ポインタの基本構文
// ========================================
// ポインタ変数の宣言(型名* 変数名)
int* ptr;
// アドレス取得演算子(&)— 変数のメモリアドレスを取得します
int value = 100;
ptr = &value;
// デリファレンス演算子(*)— ポインタが指す先の値を読み書きします
int x = *ptr; // ptr が指す値を読み取ります
*ptr = 200; // ptr が指すアドレスに値を書き込みます
// nullptr — ポインタが無効(何も指さない)であることを表します
ptr = nullptr;
// const ポインタ vs const へのポインタ
const int* p1 = &value; // 指す先の値を変更できません(pointer to const)
int* const p2 = &value; // ポインタ自体を別アドレスに変更できません(const pointer)
// ポインタ演算 — 配列の要素を順に参照します
int arr[3] = {1, 2, 3};
int* ap = arr; // 配列名はその先頭要素へのポインタと同等です
ap++; // 次の要素のアドレスに進みます(sizeof(int) 分だけ進む)
構文一覧
| 構文 | 概要 |
|---|---|
| 型名* 変数名; | ポインタ変数を宣言します。初期化せずに使うと未定義動作になるため、必ず初期化します。 |
| &変数名 | 変数のメモリアドレスを取得するアドレス取得演算子です。 |
| *ポインタ変数 | ポインタが指すアドレスに格納された値を取得・変更するデリファレンス演算子です。 |
| nullptr | ポインタが何も指さないことを表すヌルポインタ定数です(C++11以降)。 |
| const 型名* ptr | 指す先の値を変更できない「constへのポインタ(pointer to const)」です。 |
| 型名* const ptr | ポインタ自体が指すアドレスを変更できない「constポインタ(const pointer)」です。 |
| 配列名(ポインタとの関係) | 配列名は先頭要素へのポインタとして扱えます。arr[i] は *(arr + i) と等価です。 |
| ptr++ / ptr-- | ポインタを1要素分(sizeof(型)バイト)だけ前後に移動するポインタ演算です。 |
サンプルコード
pointers.cpp
// ========================================
// pointers.cpp — ポインタの基本操作サンプルです
// DEATH NOTE のキャラクターの捜査評価点を
// ポインタで操作します
// ========================================
#include <iostream>
#include <string>
// 捜査評価点を表示する関数です(ポインタ渡し)
void printScore(const std::string* name, const int* score) {
std::cout << *name << " の捜査評価点: " << *score << std::endl;
}
// 捜査評価点を更新する関数です(ポインタ渡しで直接書き換えます)
void updateScore(int* score, int newValue) {
*score = newValue;
}
int main() {
// 捜査評価点をポインタで管理します
std::string name1 = "夜神月";
std::string name2 = "L";
std::string name3 = "弥海砂";
int score1 = 95; // 夜神月の捜査評価点(優秀な新人捜査官)
int score2 = 99; // L の捜査評価点(世界最高の探偵)
int score3 = 78; // 弥海砂の捜査評価点(観察対象)
// アドレス取得演算子(&)でポインタを初期化します
int* ptr1 = &score1;
int* ptr2 = &score2;
int* ptr3 = &score3;
std::cout << "=== 初期状態の捜査評価点 ===" << std::endl;
printScore(&name1, ptr1);
printScore(&name2, ptr2);
printScore(&name3, ptr3);
// デリファレンス演算子(*)で値を更新します
std::cout << std::endl << "=== 評価点更新後 ===" << std::endl;
updateScore(ptr1, 88); // 夜神月の評価点が変動します
updateScore(ptr3, 85); // 弥海砂の評価点が上昇します
printScore(&name1, ptr1);
printScore(&name3, ptr3);
// ポインタ演算 — 配列で複数キャラクターを順に処理します
std::cout << std::endl << "=== 配列とポインタ演算 ===" << std::endl;
std::string members[3] = {"夜神月", "L", "ニア"};
int scores[3] = {95, 99, 91};
// 配列名はポインタとして使えます
std::string* namePtr = members;
int* scorePtr = scores;
for (int i = 0; i < 3; i++) {
std::cout << *namePtr << " の捜査評価点: " << *scorePtr << std::endl;
namePtr++; // 次の要素に進みます
scorePtr++; // 次の要素に進みます
}
// nullptr チェック — ポインタが有効か確認してから使います
std::cout << std::endl << "=== nullptr チェック ===" << std::endl;
int* nullPtr = nullptr;
if (nullPtr == nullptr) {
std::cout << "nullPtr は何も指していません(安全に確認できました)" << std::endl;
}
// const ポインタの例
std::cout << std::endl << "=== const ポインタ ===" << std::endl;
const int* constToVal = &score2; // 指す先の値を変更できません
std::cout << "L の捜査評価点(読み取り専用): " << *constToVal << std::endl;
// *constToVal = 0; // コンパイルエラー: const な値は変更できません
return 0;
}
g++ -std=c++17 pointers.cpp -o pointers ./pointers === 初期状態の捜査評価点 === 夜神月 の捜査評価点: 95 L の捜査評価点: 99 弥海砂 の捜査評価点: 78 === 評価点更新後 === 夜神月 の捜査評価点: 88 弥海砂 の捜査評価点: 85 === 配列とポインタ演算 === 夜神月 の捜査評価点: 95 L の捜査評価点: 99 ニア の捜査評価点: 91 === nullptr チェック === nullPtr は何も指していません(安全に確認できました) === const ポインタ === L の捜査評価点(読み取り専用): 99
よくあるミス: 初期化していないポインタを使う
ポインタ変数を宣言したまま初期化せずにデリファレンスすると、不定なアドレスにアクセスして未定義動作(クラッシュやデータ破壊)が発生します。宣言時に必ず nullptr か有効なアドレスで初期化します。
// NG: int* ptr; // 初期化されていません(不定な値を持ちます) *ptr = 42; // 未定義動作(クラッシュする可能性があります)
OK: 宣言時に nullptr で初期化し、使う前に null チェックを行います。
// OK: int* ptr = nullptr; // nullptr で初期化します int value = 42; ptr = &value; // 有効なアドレスを代入してからデリファレンスします *ptr = 100;
よくあるミス: ポインタ演算で配列の範囲外にアクセスする
配列の要素数を超えてポインタを進めると、配列の範囲外にアクセスして未定義動作が発生します。ポインタ演算を使う際は、必ず配列のサイズを超えないように管理します。
// NG:
int scores[3] = {95, 99, 91};
int* ptr = scores;
for (int i = 0; i <= 3; i++) { // i <= 3 だと4回ループして範囲外アクセスが発生します
std::cout << *ptr << std::endl;
ptr++;
}
OK: 配列サイズを正確に管理し、範囲内だけでアクセスします。
// OK:
int scores[3] = {95, 99, 91};
int* ptr = scores;
for (int i = 0; i < 3; i++) { // i < 3 で3要素だけアクセスします
std::cout << *ptr << std::endl;
ptr++;
}
概要
ポインタとは、変数が格納されているメモリアドレスを値として持つ変数です。&演算子で変数のアドレスを取得してポインタに代入し、*演算子(デリファレンス)でそのアドレスに格納された値を読み書きします。ポインタを関数引数に使うと、コピーを作らずに呼び出し元の変数を直接変更できるため、大きなデータの受け渡しで有効です。ポインタ演算(ptr++など)はポインタを1要素分(sizeof(型)バイト)だけ移動させるもので、配列の要素を順に処理するときに使います。また、配列名は先頭要素へのポインタとして扱えるため、arr[i] と *(arr + i) は同じ意味になります。初期化されていないポインタや解放済みのメモリを指すポインタを使うと未定義動作が発生するため、使用前に必ずnullptrチェックを行い、常に有効なアドレスを指すよう管理することが重要です。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。