ポインタとメモリのアドレス
このトピックを終えると
ポインタが「メモリのアドレスを格納する変数」であること、そしてそれがコンピュータサイエンスにおいてなぜ重要なのか、高水準言語ではどのように隠されているのかを理解できるようになります。
変数はどこにあるのか
プログラムが実行されると、変数はメモリに格納されます。メモリはバイト単位で番号付けされています。この番号がメモリのアドレスです。
メモリのアドレス: 0x1000 0x1004 0x1008 0x100C
┌──────┐┌──────┐┌──────┐┌──────┐
│ 42 ││ 7 ││ 'A' ││ 0 │
└──────┘└──────┘└──────┘└──────┘
変数 a 変数 b 変数 c 変数 da = 42と記述すると、コンピュータはメモリのどこかに42を格納し、aという名前をそのアドレスに関連付けます。私たちはアドレスを気にする必要はなく、aという名前を使うだけで済みます。
ポインタ - アドレスを格納する変数
通常の変数は値を格納します。ポインタは別の変数のアドレスを格納します。
int a = 42; // aに42を格納
int *p = &a; // pにaのアドレスを格納
printf("%d\n", a); // 42 (aの値)
printf("%p\n", p); // 0x1000 (aのアドレス)
printf("%d\n", *p); // 42 (pが指す場所の値)変数 a ポインタ p
┌──────────┐ ┌──────────┐
│ 42 │ │ 0x1000 │──→ aを指す
└──────────┘ └──────────┘
アドレス: 0x1000 アドレス: 0x2000&a- aのアドレスを取得(アドレス演算子)*p- pが指す場所の値を取得(間接参照)
なぜポインタが必要なのか
1. 大きなデータを効率的に渡す
1,000万個の数値を持つ配列を関数に渡すとします。配列全体をコピーすると、メモリと時間が無駄になります。アドレスだけを渡せば、元のデータに直接アクセスできます。
2. 動的なデータ構造
連結リスト、ツリー、グラフ - これらのデータ構造は、「次のノードの位置」を格納する必要があります。その「位置」がポインタです。
ノード A ノード B ノード C
┌────┬────────┐ ┌────┬────────┐ ┌────┬──────┐
│ 10 │ 0x2000 │──→│ 20 │ 0x3000 │──→│ 30 │ NULL │
└────┴────────┘ └────┴────────┘ └────┴──────┘3. 関数で元のデータを修正する
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int x = 10, y = 20;
swap(&x, &y);
// x = 20, y = 10ポインタなしで値をコピーして渡すと、関数内で値を変更しても元のデータは変更されません。ポインタでアドレスを渡さないと、元のデータを修正できません。
危険なポインタ
ヌルポインタ
int *p = NULL;
printf("%d", *p); // クラッシュ!(Segmentation Fault)何も指していないポインタを間接参照すると、プログラムがクラッシュします。これがヌルポインタの間接参照 - 歴史上最もコストのかかるバグの1つです。
ダングリングポインタ
int *p = malloc(sizeof(int));
*p = 42;
free(p); // メモリを解放
printf("%d", *p); // すでに解放されたメモリ - 予測不可能freeでメモリを解放した後に、そのアドレスにアクセスすると、運が良ければゴミの値が出力され、運が悪いとクラッシュします。
高水準言語におけるポインタ
Python、JavaScript、Javaなどの高水準言語では、ポインタを直接扱うことはできません。しかし、**参照(reference)**という名前で、同じ概念が使用されています。
# Pythonにおける参照a = [1, 2, 3]b = a # bは同じリストを「参照」b.append(4)print(a) # [1, 2, 3, 4] - 元のデータも変更されるPythonの変数は、実際にはポインタです。ただし、&や*などの構文はなく、メモリのアドレスを直接操作することはできません。以前のPythonのトピック(可変オブジェクトの参照伝播)で扱った問題が、まさにこれです。
| Cポインタ | Python参照 | |
|---|---|---|
| アドレスへの直接アクセス | 可能 | 不可能 |
| ポインタ演算 | 可能 (p + 1) | 不可能 |
| NULLクラッシュ | 可能 | None → AttributeError |
| メモリの手動解放 | 必須 (free) | 自動(ガベージコレクション) |
高水準言語は、ポインタを隠蔽し、安全にしたものです。その代償として、メモリを直接制御する能力を犠牲にします。
結論
ポインタはメモリのアドレスを格納する変数です。 大きなデータの転送、動的なデータ構造、元のデータの修正 - これらの3つの理由から、ポインタが必要になります。 Python/JavaScriptの「参照」は、ポインタを安全に包んだもの - アドレス操作はできませんが、同じ原理で動作します。