参照とメモリ
参照の本質を理解。メモリとアドレスの基本
概要
参照(&)の動作を理解するには、
メモリとアドレスの概念を知る必要があります。
この章では、変数がメモリのどこに保存されているか、 参照はそれをどのように利用するかを学びます。
メモリとアドレス
コンピュータのメモリは線形に並んだバイト単位のセルです。 各セルには固有のアドレス(メモリ番地)があります。
イメージ:
アドレス: 1000 1004 1008 1012 1016
| | | | |
値: [42] [0] [35] [0] [88]
(int) ---- (int) ---- (int)
int型の変数は通常4バイトなので、 アドレス1000から1003までを占めます。
アドレス演算子(&)
& は参照渡しの記号であり、同時にアドレス演算子でもあります。
変数のメモリアドレスを取得します。
#include <iostream>
int main() {
int x = 42;
std::cout << "値: " << x << std::endl;
std::cout << "アドレス: " << &x << std::endl;
return 0;
}
値: 42
アドレス: 0x7ffc8b4ef4ac
0x7ffc8b4ef4acのような16進数がメモリアドレスです。
実行するたびに異なる値になります。
参照とは何か
参照は、変数のアドレスを保存し、 それを通じて元の変数にアクセスします。
#include <iostream>
int main() {
int original = 100;
int & ref = original; // ref は original への参照
std::cout << "original: " << original << std::endl;
std::cout << "ref: " << ref << std::endl;
std::cout << "&original: " << &original << std::endl;
std::cout << "&ref: " << &ref << std::endl;
ref = 200; // ref を通じ て original を変更
std::cout << "original: " << original << std::endl;
return 0;
}
original: 100
ref: 100
&original: 0x7ffc8b4ef4ac
&ref: 0x7ffc8b4ef4ac
original: 200
ref と original は同じアドレスを指しています。
参照の特性
1. 参照はエイリアス
int x = 10;
int & ref = x; // ref は x のエイリアス(別名)
ref++;
std::cout << x << std::endl; // 11(ref を通じて x が変更された)
2. 参照は初期化時に決定
int a = 10, b = 20;
int & ref = a; // ref は a を参照
ref = b; // これは ref に b のアドレスを割り当てるのではなく
// a に b の値を代入する
std::cout << a << std::endl; // 20
std::cout << &ref << std::endl; // &a と同じ
3. 参照は null になれない
int & ref; // エラー! 参照は初期化が必須
int & ref = nullptr; // エラー! 参照は null になれない
参照渡しの本質
参照渡しは、実はアドレスを渡しているのですが、 ユーザーから見えない形で行われます。
#include <iostream>
void modify(int & x) {
x = 999;
}
int main() {
int value = 10;
std::cout << "変更前: " << value << std::endl;
modify(value); // value のアドレスが渡される
std::cout << "変更後: " << value << std::endl;
return 0;
}
変更前: 10
変更後: 999
modify関数はvalueのアドレスを受け取り、
それを通じて元の値を変更します。
実践例:複数の値の交換
#include <iostream>
void swapInts(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;
swapInts(x, y); // x と y のアドレスが渡される
std::cout << "交換後: x=" << x << ", y=" << y << std::endl;
return 0;
}
交換前: x=10, y=20
交換後: x=20, y=10
参照と const
例1: const参照(読取専用)
int x = 10;
const int & ref = x;
std::cout << ref << std::endl; // OK: 読取可能
// ref = 20; // エラー!: const 参照は変更不可
例2: const変数への参照
const int x = 10;
int & ref = x; // エラー!: const を非const参照に割り当てできない
const int & ref = x; // OK: const参照なら OK
参照の活用パターン
| パターン | 形式 | 用途 |
|---|---|---|
| 入力のみ | const Type & |
値を読むだけ、変更しない |
| 入出力 | Type & |
値を変更する |
| 出力のみ | Type & |
関数内で値を設定する |
| 効率的な読取 | const Type & |
大きなデータをコピーしない |
ポイント
- 参照本質はエイリアス(別名)
&はアドレス演算子でもあり、参照宣言でもある- 参照は初期化時に決定され、その後参照先を変更できない
- 参照は自動的にデリファレンスされる(ユーザーは意識しない)
- const参照は効率的で安全な入力方法
よくある誤り
誤り1: 参照を変更しようとする
int a = 10, b = 20;
int & ref = a;
ref = b; // b に参照を変更するのではなく、a に b の値を代入
誤り2: const参照への代入
int x = 10;
const int & ref = x;
ref = 20; // エラー!: const 参照は変更不可
誤り3: スコープ外への参照を返す
int & getRef() {
int local = 10;
return local; // 危険!: local はここで破棄される
}
やってみよう
練習1: 3つの整数を受け取り、ソートする関数(参照渡し)。
練習2: 配列の各要素を参照渡しで増加させる関数。
練習3: Min/Max を参照で返す関数。
チャレンジ: テンプレート関数を使ったデータ型別の交換関数。
まとめ
- 参照は変数のエイリアス、メモリ効率的でパワフル
- 参照本質を理解があれば、パワーあるC++コードを書ける
- const参照は入力パラメータの標準パターン
- 参照は初期化時に決定され、その後変更できない(ポインタとの違い)