C++のアドレス演算子(&)と間接演算子(*)を徹底解説!ポインタの仕組みをマスター
生徒
「先生、C++のポインタに出てくる『&』と『*』がややこしくて混乱しています。どちらも記号だし、何が違うのかさっぱりで……。」
先生
「確かに、最初は戸惑いますよね。これらは『住所を調べる道具』と『その住所にある中身を見る道具』というペアだと考えると分かりやすいですよ。」
生徒
「住所と中身、ですか。具体的にはどう使い分けるんですか?」
先生
「それでは、ポインタ操作の基本となる2つの演算子について、詳しく見ていきましょう!」
1. アドレス演算子(&)とは?「場所」を特定する記号
C++のアドレス演算子(&)とは、変数という「箱」が置かれているメモリ上の住所(アドレス)を取得するための記号です。プログラミング未経験の方には、「住所を検索するボタン」だとイメージしていただくと分かりやすいでしょう。
パソコンを動かしているとき、数値や文字などのデータは「メモリ」という場所に保存されています。このメモリは巨大なロッカーのようなもので、一つ一つの扉に番号(住所)が振られています。私たちが int price = 100; と書くと、コンピューターはどこかのロッカーを選んで 100 を入れます。このとき、&price と書くことで、「priceが入っているロッカーの番号は何番ですか?」と問いかけることができるのです。
2. 間接演算子(*)とは?「中身」を操作する記号
一方、間接演算子(*)は、取得した住所(ポインタ)を頼りにして、その場所に実際に入っているデータを読み書きするための記号です。ポインタの世界では「デリファレンス」とも呼ばれます。住所だけ知っていても中身が見られなければ意味がないため、この演算子を使ってロッカーの扉を開けるわけです。
例えば、「東京都中央区1-2-3」という住所(ポインタ)が書かれた紙があるとします。これだけでは、そこにどんな家が建っているか分かりません。間接演算子は、その住所へ実際に行ってみて、「中には誰が住んでいますか?」と確認したり、「壁の色を塗り替えなさい」と命令したりする行動そのものを指します。
3. コードで見る「住所の取得」と「値の操作」
実際に、アドレス演算子 & と間接演算子 * を組み合わせた基本的なプログラムを見てみましょう。このペアの使い方を覚えることが、C++エンジニアへの第一歩です。
#include <iostream>
int main() {
int apple = 10; // 普通の変数
int* ptr = &apple; // &を使ってappleの「住所」を取得し、ポインタptrに保存
std::cout << "appleの住所: " << ptr << std::endl;
std::cout << "住所にある中身: " << *ptr << std::endl; // *を使って住所の「中身」を表示
return 0;
}
appleの住所: 0x7ffd5e3a2b14 (※環境によって異なります)
住所にある中身: 10
このように、& で住所を取り出し、その住所に対して * を使うことで、元の数値 10 を取り出すことができました。この一連の流れが、C++のメモリ管理の基本となります。
4. 間接演算子(*)によるデータの書き換え
間接演算子は、単に中身を見るだけではありません。その場所にあるデータを書き換えることもできます。これは、元の変数名を使わずに、遠隔操作で値を変更するようなイメージです。
パソコンを触ったことがない方でも、テレビのリモコンを想像してください。テレビ本体のボタンを直接押さなくても、リモコン(ポインタ)を通して「チャンネルを変える(間接演算子による操作)」ことができますよね。実際のコードで確認してみましょう。
#include <iostream>
int main() {
int score = 50;
int* pScore = &score; // scoreの住所をメモ
// 間接演算子を使って、住所にあるスコアを更新する
*pScore = 100;
std::cout << "元のscore変数の値: " << score << std::endl;
return 0;
}
元のscore変数の値: 100
score = 100; と直接書かなくても、ポインタ経由で数値が変わっているのが分かります。これが、間接演算子の強力な機能です。
5. 記号「*」の二つの顔に注意!
初心者が最も混乱するのは、アスタリスク(*)が二通りの意味で使われることです。これを見分けるのが、挫折しないための最大のコツです。
- 宣言の「*」:
int* ptr;のように、変数の種類(型)を決めるときに使います。これは「この変数は住所専用の箱ですよ」という意味です。 - 間接演算子の「*」:
*ptr = 20;のように、計算や代入の中で使います。これは「その住所の扉を開けろ」という命令(動作)を指します。
英語で言えば、同じ単語でも「名詞」として使われるか「動詞」として使われるかで意味が変わるようなものです。文脈の中で判断する練習をしましょう。
6. アドレスと間接演算子の関係をイメージ図で整理
これら二つの演算子の関係を図で整理してみましょう。C++におけるメモリのやり取りは、常にこの往復で成り立っています。
変数の箱から住所を取り出すのが &、住所を頼りに箱へ戻るのが * です。もし *(&変数) と書けば、住所に行って戻ってくるだけなので、元の変数と同じ意味になります。この「あべこべ」の関係性が分かると、複雑なプログラムを読んでも迷わなくなります。プログラミング初心者の方は、まずこの「往復の関係」を頭に叩き込みましょう。
7. 安全に使うためのルール:ヌルポインタ
間接演算子 * を使うとき、絶対にやってはいけないことがあります。それは、「存在しない住所(デタラメな番号)」の扉を開けようとすることです。これをやってしまうと、プログラムは即座に壊れてしまいます。
安全を確保するために、どこも指していないことを示す nullptr(ヌルポインタ)という特別な状態があります。間接演算子を使う前に、「この住所録は白紙じゃないかな?」と確認する癖をつけることが、一流のプログラマーへの近道です。
#include <iostream>
int main() {
int* dataPtr = nullptr; // 最初はどこも指さないように初期化
// 住所が入っているか確認してから中身を触る
if (dataPtr != nullptr) {
std::cout << *dataPtr << std::endl;
} else {
std::cout << "住所が登録されていません。" << std::endl;
}
return 0;
}
8. C++のメモリ管理を支えるポインタの重要性
なぜこんな面倒な記号を使うのでしょうか?それは、C++がコンピューターの性能を最大限に引き出すための言語だからです。巨大な写真や動画のデータを、いちいちコピーして別の場所に運ぶのは効率が悪いです。代わりに、「あのロッカーにデータがあるから、そこを見てね」と住所(ポインタ)だけを教えれば、動作が非常に高速になります。
アドレス演算子と間接演算子は、コンピューターという巨大な倉庫を効率よく管理するための「検索システム」と「鍵」のような存在です。最初は難しく感じるかもしれませんが、この二つの記号を使いこなせるようになれば、あなたはコンピューターを直接操っているという実感を得られるはずですよ!