<目次>
(1) ポインタと参照の違いについてサンプルPGを使ってご紹介
(1-1) ポインタと参照の概要
(1-2) 両者の違い①:宣言/初期化
(1-3) 両者の違い②:再代入
(1-4) 両者の違い③:メモリ領域
(1-5) 両者の違い④:間接参照
(1-6) 「ポインタ」の補足
(1-7) 「ポインタ」と「参照」の主な使いどころ
(1) ポインタと参照の違いについてサンプルPGを使ってご紹介
(1-1) ポインタと参照の概要
●ポインタ
●参照
(1-2) 両者の違い①:宣言/初期化
●ポインタ
int i1 = 10; int *ptr1 = &i1; //# あるいは以下のように分けて記述可能 //# (OK↓) //# int *ptr1; //# ptr1 = &i1;
●参照
int i1 = 10; int &rfr1 = i1; //# ちなみに、参照の場合は変数定義と代入を分けて書くのはNG //# (NG↓) //# int &rfr1; //# rfr1 = i1;
●サンプルプログラム
#include <time.h> #include <iostream> using namespace std; int main () { int i1 = 10; //# ポインタの宣言/初期化 int *ptr1 = &i1; //# (1)&ptr1: ポインタ自体のアドレス //# (2)*ptr1: ポインタが参照するアドレスに格納された値(デリファレンス) //# (3) ptr1: ポインタが参照するアドレス値(リファレンス) cout << &ptr1 << " - " << *ptr1 << " - " << ptr1 << endl; //# 参照の宣言/初期化 int &rfr1 = i1; //# (4)&rfr1: 参照先のアドレス //# (5)*&rfr1: 参照先のアドレスに格納された値(デリファレンス) //# (6) rfr1: 参照先の値 cout << &rfr1 << " - " << *&rfr1 << " - " << rfr1 << endl; //# (参考) 「*rfr1」はエラーとなります(値10に対するデリファレンスのため) return 0; }
0x7ffc225682b8 - 10 - 0x7ffc225682c4 0x7ffc225682c4 - 10 - 10
(1-3) 両者の違い②:再代入
●ポインタ
int a1 = 9; int a2 = 7; int *ptr; ptr = &a1; ptr = &a2;
●参照
int a1 = 9; int a2 = 7; int &rfr = a1; int &rfr = a2; //# この行でエラーになる
●サンプルプログラム
#include <time.h> #include <iostream> using namespace std; int main () { int a1 = 9; int a2 = 7; //### ポインタ //# ポインタ定義 int *ptr; //# 代入:「a1」のアドレスを代入 ptr = &a1; cout << &ptr << endl << &a1 << endl; //# 再代入:「a2」のアドレスを代入 ptr = &a2; cout << &ptr << endl << &a1 << endl; //### 参照 //# 参照定義&「a1」を代入 int &rfr = a1; cout << &rfr << endl << &a1 << endl; //# 参照定義&「a2」を代入(▲コンパイルエラー) int &rfr = a2; //# この行でエラーになる cout << &rfr << endl << &a1 << endl; return 0; }
(実行結果)→参照に再代入しようとしたためコンパイルエラー
[admin@ik1-411-37776 tmp_rainbow]$ g++ -g -o ./ReferenceVsPointer2 ./ReferenceVsPointer2.cpp ./ReferenceVsPointer2.cpp: In function 'int main()': ./ReferenceVsPointer2.cpp:20:10: error: redeclaration of 'int& rfr' int &rfr = a2; ^ ./ReferenceVsPointer2.cpp:18:10: error: 'int& rfr' previously declared here int &rfr = a1;
(図132)
(1-4) 両者の違い③:メモリ領域
次はメモリ領域に関する、ポインタと参照の違いです。
●ポインタ
元の変数とは別にメモリ領域を使用します。以下のコードで「&ptr」がポインタ自身のアドレス、「&a1」はポインタが指し示す変数のアドレスで、両者は異なる値になっています。
int a1 = 9; int *ptr = &a1; //# 異なるアドレスになる cout << &ptr << endl << &a1 << endl
●参照
元の変数と同じアドレスを使用します(一部スタック領域も使用します)。そのため、参照のアドレスである「&rfr」と、参照先の変数のアドレス「&a1」は同じ値になっています。
int a1 = 9; int &rfr = a1; //# 同じアドレスになる cout << &rfr << endl << &a1;
●サンプルプログラム
(サンプル)
#include <time.h> #include <iostream> using namespace std; int main () { int i1 = 10; //# ポインタの宣言/初期化 int *ptr1 = &i1; //# (1)&ptr1: ポインタ自体のアドレス //# (2)&il: 変数のアドレス //# →異なるアドレスになる cout << &ptr1 << endl << &i1 << endl; //# 参照の宣言/初期化 int &rfr1 = i1; //# (3)&rfr: 参照自体のアドレス //# (4)&il: 変数のアドレス //# →同じアドレスになる cout << &rfr1 << endl << &i1 << endl; //# <想定結果> //# (1)≠(2)=(3)=(4) return 0; }
(図141)
(実行結果)
0x7ffc98463118 0x7ffc98463124 0x7ffc98463124 0x7ffc98463124
(図142)
(1-5) 両者の違い④:間接参照
●ポインタ
int a1 = 10; int *ptr1 = &a1; int **ptr2 = &ptr1;
●参照
int a1 = 10; int &rfr1 = a1; int &&rfr2 = rfr1; //# エラーになる
●サンプルプログラム
(サンプル)
#include <time.h> #include <iostream> using namespace std; int main () { int a1 = 10; //### ポインタは多重にする事が可能 //# ポインタを定義(OK) int *ptr1 = &a1; cout << &ptr1 << endl << &a1 << endl; //# ポインタのポインタを定義(OK) int **ptr2 = &ptr1; cout << &ptr2 << endl << &a1 << endl; //### 参照は多重に出来ない //# 参照を定義(OK) int &rfr1 = a1; cout << &rfr1 << endl << &a1 << endl; //# 参照の参照を定義(NG) int &&rfr2 = rfr1; //# エラーになる cout << &rfr2 << endl << &a1 << endl; return 0; }
(図151)
(実行結果)
./ReferenceVsPointer4.cpp: In function 'int main()': ./ReferenceVsPointer4.cpp:18:9: error: expected unqualified-id before '&&' token int &&rfr2 = rfr1; //# Error at this line ^ ./ReferenceVsPointer4.cpp:19:14: error: 'rfr2' was not declared in this scope cout << &rfr2 << endl << &a1 << endl;
(図152)
(1-6) 「ポインタ」の補足
(※注1)ポインタ演算とは?
C++ではポインタに対して、int型の「足し算」や「引き算」をする事が可能です。もしポインタ「ptr1」がint型のアドレスを保持している場合、「ptr1+1」は「ptr1」の後の次のint型のアドレスを指し示します。
(例)
int var[SIZE] = {100, 1000, 10000}; int *ptr1; ptr1 = var; ptr1++; //# *ptr1 = 0xceb099c0 ptr1++; //# *ptr1 = 0xceb099c4 ptr1++; //# *ptr1 = 0xceb099c8
この例では「ptr1++」する毎に「ptr1」が指すアドレスが「0xceb099c0」→「0xceb099c4」→「0xceb099c8」のように変化していきます(int型は4バイトのため、0→4→8)。
(1-7) 「ポインタ」と「参照」の主な使いどころ
●ポインタの使いどころ
・ポインタ演算をしたり、NULL値を代入する必要があるケースで使用します
(例:配列など。配列へのアクセスはポインタ演算で実装されている)
・LinkedListといったデータ構造を実装する際に使用します。
→異なるアドレスを指し示すためには、ポインタが必要となる(再代入なども必要となるため参照ではNG)
●参照の使いどころ
・関数の「引数」や「戻り値」で使用します
(再代入やNULL代入などが必要ない引数等においては、参照を使う事ができます)