現在の言語: 日本語

戻る

参照渡し
参照渡し

参照渡しについて
デフォルトは値渡しですが
変数の前に&(アンパサンド)をつけると参照渡しとなります。
C言語ではポインタとポインタへのポインタ渡しは下記の概要となります。
構文意味PHPから見た機能的な類似点
test(*handle)ポインタの値渡しPHPの値渡し
test(**handle)ポインタへのポインタ渡しPHPの参照渡し(test(&$handle))
仮にhandle変数をopen関数で実行したハンドルの実態と仮定します。
*handle
*ポインタなのでハンドルの実態にアクセスするということになります。
**は*(ハンドル)の実態が入っている変数へのアクセスということになります。
そのため**handleにアクセスするということはハンドルの実態が入っている変数そのものに
アクセスするということになります。

[C言語の例]
ポインタの値渡し

copy
void increament(int *num)
{
  //ポインタが指す先の値を操作します
  (*num)++;
}
int main()
{
  int num = 10;
  increment(&num);//11
}
copy
void increament(int *num)
{
	//Operates on the value pointed to by the pointer
	(*num)++;
}
int main()
{
	int num = 10;
	increment(&num);//11
}

increament関数内ではnumのアドレスを保持するポインタのコピーが使われます。
ポインタが指す場所(numのメモリ領域)はオリジナルと同じ場所です。
そのためデータ本体の書き換えが可能です。

ポインタへのポインタ渡し
(例)
C言語:**num
PHP:&$num
呼び出し元のポインタ変数が保持しているアドレス自体を書き換える処理がポインタへのポインタ渡しとなります。
[C言語の例]


copy
#include//for malloc
void test(int **num)
{
  //新しいメモリ領域を確保し
  // sonoadoresuwo 
  呼び出し元のnum変数に直接書き込みます
  *num =(int*)malloc(sizeof(int));
  if(*num != NULL)
  {
    **num = 10;
  }
}
int main()
{
  int *num = NULL;//ポインタ変数を宣言
  //ポインタ変数(num)自体のアドレスを渡します
  test(&num);//10
}
copy
#include //for malloc
void test(int **num)
{
	//Allocate new memory area
	// Write directly to the caller's num variable
	*num = (int*)malloc(sizeof(int));
	if(*num != NULL)
	{
		**num = 10;
	}
}
int main()
{
	int *num = NULL; //Declare pointer variable
	//Pass the address of the pointer variable (num) itself
	test(&num); //10
}
main関数で宣言したnum変数にアクセスして
その中身(ポインタが保持するアドレス)自体を完全に新しい値に置き換えています。
関数内で新しいメモリ確保やリソースの初期化を行っています。
その結果を呼び出して、元の変数に反映させることができます。
項目ポインタの値渡し(*)ポインタへのポインタ渡し(**)
目的データ本体の読み書きポインタ変数自体の書き換え
PHPでの相当$variable(値渡し)&$variable(参照渡し)
用途既存データを変更します関数内で変数を初期化・置き換えます
[値渡し]
変数名メモリ上の場所格納されている値
$a(呼び出し元)アドレスA10
$b(関数内引数)アドレスB10(コピー)
[値渡し]


copy
function test(int $b)
{
  //$bは10から11に変更されます
  //しかし、$aの変数の値が変わっているわけではありません
  $b++;
}
$a =10;
test($a);
copy
function test(int $b)
{
	//$b changes from 10 to 11
	//However, the value of the $a variable does not change.
	$b++;
}
$a = 10;
test($a);
test関数を実行します。
test関数が実行された時点で
$aとは別のメモリ領域に$aの10がコピーされます。
そのため関数内で$bの値を変更しても
呼び出し元の$aには影響がありません。

PHPエンジンではCOW(Copy-On-Write)が効率化のために実行されます。
$aと$bは同じメモリ領域を共有しています。
関数内で$bに新しい値が代入されたタイミングで初めてメモリのコピーが発生します。
そのため新しい値が代入されないということは
データの変更がないということなので
無駄なコピーは発生しないため軽い動作を実現しています。

[参照渡し]


copy
function test(int &$b)
{
  //$bは20から21に変更されます
  //しかし、$aの変数の値が変わっているわけではありません
  $b++;
}
$a =20;
test($a);
copy
function test(int &$b)
{
	//$b changes from 20 to 21
	//However, the value of the $a variable does not change.
	$b++;
}
$a = 20;
test($a);
変数名メモリ上の場所格納されている場所/リンク先
$a(呼び出し元)アドレスA10
$b(関数内引数)アドレスBアドレスAへのリンク
test関数内の引数$bという変数が新しく作成されます。
しかし、&$bの「&」は参照なので$aを指す指示がでています。
そのため$aのデータをコピーするという作業は発生しません。
そのためtest関数内で$b変数を変更すると
リンク先の$aのアドレスにある値が直接変更するため
20から21に変更されます。
そのためtest関数が終わると$aの値が21に変わっていることを確認できます。
項目値渡し($a)参照渡し(&$a)
データのコピー書き換え時に発生(COW)発生しない
メモリ消費理論上増えますがCOWで抑制ほとんど増えません
関数外への影響なしあり(変数自体が書き換わります)
速度参照よりわずかに速い傾向値渡しよりわずかに遅い傾向
[zval]
PHPの共通のコンテナでデータ本体を管理します。
zvalには変数に入っている値そのものではなく
その値が格納されいてる内部データ構造(zval)へのアドレス(ポインタ)を管理しています。

[zvalに入っている情報]
データ本体
データへのポインタ
型情報(int/stringなど)
参照カウント
など

[COWで処理した値渡しのメモリ使用量]
変数の値変更などをすると元の値と変更後の値の両方がメモリ上に存在します。
その瞬間の最大メモリ使用量は大きくなりますが
関数を抜けると不要な領域が解放されます。

[COWで処理した参照渡しのメモリ使用量]
新しいデータ操作後のデータのための領域は確保されます。
元の領域を上書きするため、操作中のメモリ使用量は削減される可能性が高いです。

このことから参照渡しの方がメモリ効率は良いと言えます。
[理由]
値渡しでは、関数内の操作時に元のデータと変更後のデータの両方が
同時にメモリに載る瞬間が発生します。
参照渡しでは常に一つのデータ本体の領域を直接操作するため
メモリの消費は最小限に抑えられます。

[COWとガベージコレクション]
COWは参照カウントのよりメモリ解放をします。
しかし、参照カウントのみでは解放できないものを
ガベージコレクションが解放する仕組みです。


戻る

著作権情報
ホームページおよプリ等に掲載されている情報等については、いかなる保障もいたしません。
ホームページおよびアプリ等を通じて入手したいかなる情報も複製、販売、出版または使用させたり、
または公開したりすることはできません。
当方は、ホームページおよびアプリ等を利用したいかなる理由によっての障害等が発生しても、
その結果ホームページおよびアプリ等を利用された本人または他の第三者が被った損害について
一切の責任を負わないものとします。