参照
概要
参照とは変数に別名を付ける機能のことです。
参照を使うことで同じアドレスを共有する変数を複数作成することができます。
// 書式例
データ型& 変数名
// 具体例
int main()
{
int val = 0;
int& ref = val;
printf("valのアドレス = %d\n", &val);
printf("refのアドレス = %d\n", &ref);
return 0;
}
実行結果:
valとrefのアドレスが同じ値で表示される
上の具体例のように参照を使用すると別の名前の変数でもアドレスは同じ値になります。
また、アドレスが同じなので代入処理などを行うと
アドレスを共有している全ての参照変数に影響を与えます。
int main()
{
int val = 0;
int& ref = val;
printf("valに100を代入\n");
value = 100;
printf("val = %d\n", val);
printf("ref = %d\n", ref);
printf("\n");
printf("refに10を代入\n");
reference = 10;
printf("val = %d\n", val);
printf("ref = %d\n", ref);
return 0;
}
実行結果:
valに100を代入
val = 100
ref = 100
refに10を代入
val = 10
ref = 10
上のコードで変数valを変更すると変数refも変更されており、
変数refを変更すると変数valも変更されていることが分かります。
このように、参照を使用した場合はどれかの変数に変更を加えると、
アドレスを共有している変数に影響を与えます。
参照時のメモリ
次の絵は参照使用時のメモリイメージです。
この絵はあくまでイメージですが、参照を使用した場合、
一つの変数のメモリ領域に別の変数名が追加されていると考えれば理解しやすいと思います。
参照渡し
参照渡しとは関数の引数に参照を使用する方法のことです。
参照渡しの引数を関数内で変更した場合、呼び出し元の値が変更されます。
これは参照によって引数と呼び出し元の変数が同じアドレスを共有しているからです。
// 書式
// 関数側
戻り値の型 関数名(データ型& 変数名)
// 実行側
通常の引数を指定する方法のままでいい
// 具体例
void Init(int& rhp, int& rmp)
{
rhp = 50;
rmp = 10;
}
int main()
{
int hp = 0;
int mp = 0;
Init(hp, mp);
printf("HP = %d\n", hp);
printf("MP = %d\n", mp);
return 0;
}
出力結果:
HP = 50
MP = 10
出力結果からInit関数に渡す実引数hpとmpを参照渡しにして実行すると
main関数のhpとmpの値が変更されていることがわかります。
参照渡しのメモリ
次の絵は参照渡しのメモリイメージです。
参照渡しで渡された変数は関数内で別の名前として使用されます。
呼び出し元の変数と関数側の変数はメモリ領域を共有しているので
関数側で変更を行うと呼び出し元の変数の値も変更されます。
参照渡しの注意点
参照渡しは実引数の変数を別名にして仮引数として使用する方法なので
参照渡しには実体のある変数しか指定できません。
リテラルな値(直値)を指定したらエラーになります。
void Init(int& rhp, int& rmp)
{
rhp = 50;
rmp = 10;
}
int main()
{
Init(50, 10); // エラー
return 0;
}
上のコードはmain関数でリテラルな値(50と10)を実引数にして
Init関数を実行しようとしていますが、Init関数の仮引数は参照渡しです。
結果、二つの引数は共有するメモリ領域がないため実行できずにエラーとなります。
const参照渡し
参照渡しを使用して関数を実行した際に関数側で値を変更されたくない時は
仮引数の指定時に「const」を指定します。
const指定を行うことで仮引数の変数の値の変更は不可能となります。
// 書式
戻り値 関数名(const 型& 変数名)
// 具体例
void Init(const int& rhp, const int& rmp)
{
rhp = 100; // エラー
rmp = 30; // エラー
}
void Print(const int& rhp, const int& rmp)
{
printf("HP = %d\n", rhp);
printf("MP = %d\n", rmp);
}
void main(void)
{
int hp = 50;
int mp = 10;
Init(hp, mp);
PrintParam(hp, mp);
}
上のInitとPrint関数はどちらもconst参照渡しで引数を渡しています。
その結果、Initでは引数の値を変更使用しているためエラーとなり、
Printは引数の値を参照するためだけに使用しているのでエラーになっていません。
このようにconst参照を使用することで、関数先での値の書き換えが防げるので
必要と思われる場所では積極的に使用してください。