ポインタの基本

メモリアドレスを直接操作する方法。C++の最重要概念

概要

ポインタは、 メモリのアドレス(記憶場所)を保存する変数です。

C++を本格的に理解するには、 ポインタの概念が不可欠です。 難しいかもしれませんが、 段階的に学べば必ず理解できます。

アドレスとの関係

まず、変数がメモリのどこに保存されているかを理解しましょう。

#include <iostream>

int main() {
    int x = 42;
    
    std::cout << "値: " << x << std::endl;
    std::cout << "アドレス: " << &x << std::endl;
    
    return 0;
}

値: 42
アドレス: 0x7ffc123a4ff4

&xは「xのアドレス」を意味します。 このアドレスはコンピュータのメモリ上での位置です。

ポインタの宣言と初期化

ポインタの作成

#include <iostream>

int main() {
    int x = 42;
    
    // ポインタの宣言と初期化
    int* ptr = &x;  // ptr は x のアドレスを保存
    
    std::cout << "x の値: " << x << std::endl;
    std::cout << "ptr の値(アドレス): " << ptr << std::endl;
    
    return 0;
}

x の値: 42
ptr の値(アドレス): 0x7ffc123a4ff4

int* ptrは「int型へのポインタ」を意味します。 *がポインタの記号です。

ポインタのデリファレンス

ポインタが指す先の値を取得するには、 デリファレンス演算子 * を使います。

#include <iostream>

int main() {
    int x = 42;
    int* ptr = &x;
    
    std::cout << "ポインタが指す値: " << *ptr << std::endl;
    
    // ポインタを通じて値を変更
    *ptr = 100;
    std::cout << "x の値: " << x << std::endl;
    
    return 0;
}

ポインタが指す値: 42
x の値: 100

重要*ptrを変更すると、 元の変数xも変更されます。

ポインタと参照の関係

参照は「自動的にデリファレンスされるポインタ」と言えます。

参照 ポインタ
宣言 int & ref = x; int* ptr = &x;
使用 ref = 10; *ptr = 10;
初期化 必須 任意
null 可能 不可 可能
再割り当て 不可 可能

ポインタの便利な使い方

例1: 複数の変数のアドレスを管理

#include <iostream>

int main() {
    int a = 10, b = 20, c = 30;
    
    int* ptrs[3] = {&a, &b, &c};  // ポインタの配列
    
    for (int i = 0; i < 3; i++) {
        std::cout << "値: " << *ptrs[i] << std::endl;
    }
    
    return 0;
}

値: 10
値: 20
値: 30

例2: ポインタを通じた値の交換

#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;
}

交換前: x=10, y=20
交換後: x=20, y=10

nullptr:ヌルポインタ

ポインタが何も指していない場合、 nullptrを使います。

#include <iostream>

int main() {
    int* ptr = nullptr;  // 何も指していない
    
    if (ptr == nullptr) {
        std::cout << "ポインタは null です" << std::endl;
    }
    
    int x = 42;
    ptr = &x;  // 今、x を指す
    
    if (ptr != nullptr) {
        std::cout << "ポインタが指す値: " << *ptr << std::endl;
    }
    
    return 0;
}

ポインタは null です
ポインタが指す値: 42

メモリ可視化

ポインタの動作を理解するため、メモリ図を見てみましょう。

メモリアドレス  値       説明
0x1000         [42]    変数 x
0x1004         [0x1000] ポインタ ptr(x のアドレスを保存)

ptr が指す先 = x
*ptr デリファレンス = 42
                

ポインタと配列

配列の名前は、実は配列の最初の要素へのポインタです。

#include <iostream>

int main() {
    int arr[3] = {10, 20, 30};
    int* ptr = arr;  // arr は &arr[0] と同じ
    
    std::cout << ptr[0] << std::endl;      // 10
    std::cout << ptr[1] << std::endl;      // 20
    std::cout << *(ptr + 2) << std::endl;  // 30
    
    return 0;
}

10
20
30

ptr + 2は「ptr の指す位置から2つ先」を意味します。

ポイント

  • ポインタはメモリアドレスを保存する変数
  • &(アドレス演算子)で変数のアドレスを取得
  • *(デリファレンス演算子)でポインタが指す先の値を取得
  • nullptrで「何も指していない」を表現
  • 配列は、実はポインタの特殊形態

よくある誤り

誤り1: 初期化されていないポインタを使う

int* ptr;       // 初期化されていない
std::cout << *ptr << std::endl;  // 危険!: ゴミ値

誤り2: nullptr をデリファレンス

int* ptr = nullptr;
std::cout << *ptr << std::endl;  // クラッシュ

誤り3: ポインタと値を混同

int x = 42;
int* ptr = &x;

std::cout << ptr << std::endl;   // アドレスが出力
std::cout << *ptr << std::endl;  // 値が出力

やってみよう

練習1: 2つの変数のアドレスを表示するプログラム。

練習2: ポインタを使って2つの値を交換する関数。

練習3: 配列のポインタを使って要素にアクセス。

チャレンジ: ポインタ配列を使って複数の変数を管理。

まとめ

  • ポインタは C++で最重要の概念
  • メモリの直接操作が可能になり、パワーが大幅に増加
  • &*の関係を深く理解
  • 安全性に注意(nullptr チェック、範囲確認)