はじめに
前回の記事では、「配列の名前は、配列の先頭要素を指すポインタのように振る舞う」というC++の重要な性質を学びました。では、その「ポインタのようなもの」に対して、もし足し算や引き算をしたら、一体何が起こるのでしょうか?
これが、今回解説する「ポインタ演算」の世界です。ポインタ演算を理解すると、なぜfor
文で配列の全要素にアクセスできるのか、その裏側の仕組みが手に取るように分かります。
ポインタに1を足すと、アドレスはどうなる?
素直に考えると、メモリアドレス(数値)に1
を足すのだから、「アドレスの値が1だけ増える」と思ってしまいますよね。しかし、C++の世界ではそうはなりません。
ポインタ演算の最も重要なルールはこれです。
ポインタに整数
n
を足すと、アドレスはn
バイト進むのではなく、「指し示しているデータ型n
個分」進む。
int
型の配列を指すポインタに1
を足した場合、int
型1個分のサイズ(多くの環境で4バイト)だけアドレスが進みます。つまり、配列の次の要素を指すようになるのです。
これを、アパートの部屋番号で考えてみましょう。ポインタは「部屋の住所」を知っています。ポインタに+1
するということは、「隣の部屋の住所」を尋ねるのと同じことなのです。
実際のコードでポインタ演算を確認
C++コード例
このルールが本当かどうか、実際のコードで確かめてみましょう。numbers
という配列を使って、ポインタ演算の結果がどのようになるかを出力します。
#include <iostream>
using namespace std;
int main()
{
int numbers[5] = {100, 200, 300, 400, 500};
cout << "--- ポインタ演算の基本 ---\n\n";
// 配列名「numbers」は先頭要素のアドレスを指すことを再確認
cout << "numbers のアドレス:\t" << numbers << "\n";
cout << "&numbers[0] のアドレス:\t" << &numbers[0] << "\n\n";
// 「numbers + 1」でアドレスがどう変わるかを確認
cout << "numbers + 1 のアドレス:\t" << numbers + 1 << "\n";
cout << "&numbers[1] のアドレス:\t" << &numbers[1] << "\n\n";
// ポインタ演算の結果をデリファレンス(*で値を取得)
cout << "*(numbers + 1) の値:\t" << *(numbers + 1) << "\n";
cout << "numbers[1] の値:\t" << numbers[1] << "\n";
return 0;
}
結果の解説
このコードを実行すると、以下のような結果が得られます。(アドレス値は実行環境により異なります)
--- ポインタ演算の基本 ---
numbers のアドレス: 0x7ffee...a0
&numbers[0] のアドレス: 0x7ffee...a0
numbers + 1 のアドレス: 0x7ffee...a4
&numbers[1] のアドレス: 0x7ffee...a4
*(numbers + 1) の値: 200
numbers[1] の値: 200
- 1つ目のブロック:
numbers
と&numbers[0]
が同じアドレスであることを再確認しています。 - 2つ目のブロック: ここが核心です。
numbers + 1
の結果は、numbers
のアドレスに単純に1を足した...a1
にはならず、...a4
となっています。これはint
型が4バイトの環境で、きっちりデータ1個分アドレスが進んだことを示しています。そして、そのアドレスは&numbers[1]
(2番目の要素のアドレス)と完全に一致します。 - 3つ目のブロック:
numbers + 1
が2番目の要素を指すアドレスなので、*(numbers + 1)
とデリファレンスすれば、2番目の要素の値200
が取得できます。これはもちろんnumbers[1]
の値と同じです。
[]
と *()
の驚くべき関係
ここまでの解説で、鋭い方は気づいたかもしれません。
numbers[1]
*(numbers + 1)
この2つの書き方は、全く同じ意味だったのです。
一般化すると、配列名[n]
という見慣れた書き方は、C++の内部では *(配列名 + n)
というポインタ演算の形で解釈されています。普段何気なく使っている角括弧[]
は、実はポインタ演算を分かりやすくするための「シンタックスシュガー(糖衣構文)」だった、というわけです。
まとめ
今回は、C++の強力な機能である「ポインタ演算」の基本を解説しました。
- ポインタに整数
n
を足すと、n
個先の要素を指すアドレスになる。 - これにより、配列の各要素を順番に辿ることができる。
配列名[n]
という書き方は、内部的には*(配列名 + n)
として処理されている。
このポインタ演算の仕組みこそが、配列とポインタの密接な関係性の核心です。この基本を理解すれば、C++のメモリ操作に対する解像度が格段に上がるはずです。