<目次>
(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代入などが必要ない引数等においては、参照を使う事ができます)