C++: C++ ポインタ

最終更新:2026-08-31

これまで、変数はメモリ上の「箱」のようなものだと学びました。

でも、その「箱」がメモリ上のどこにあるか知りたい場合は?

ポインタは変数のアドレスを格納する特別な変数 —— メモリの場所を指し示します。


1. ポインタとは?

(1) 1.1 ポインタの役割

役割 プログラムでの用途
変数のアドレスを格納("どこにあるか") ポインタ(アドレス)
アドレス先の値を取得("何が入っているか") 変数(アドレス先のメモリ)

ポインタの定義: 変数のアドレスを格納する変数。

(2) 1.2 なぜポインタが必要?

  1. 動的メモリ確保new / delete
  2. 関数で変数を変更(ポインタを経由)
  3. 配列の操作(配列とポインタの関係)
  4. データ構造の実装(連結リスト、ツリー、グラフ)


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;
}
💡 ヒント: *px と同じ値を返します!


(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) // Linux
TEXT 📖 参照専用
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
  • ヌルポインタや未初期化ポインタの間接参照は危険

📝 練習問題

  1. 初級(難易度 ⭐): int 変数 x = 10 を作り、int* ポインタ px を指してください。x の値と *p の値を出力してください。

  2. 中級(難易度 ⭐⭐): 関数 void addTen(int* p) を書いてください。ポインタが指す変数に10を加算します。main でテストしてください。

  3. 上級(難易度 ⭐⭐⭐): プログラムを書いてください。3つの int 変数 a, b, c と3つのポインタを作ります。ポインタ経由で変数の値を100に設定し、出力してください。



9. 🚀 次のステップ

ポインタの基本を理解したら、次は ポインタと配列(レッスン23) —— 配列とポインタの深い関係、ポインタ演算を学ぼう!

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%