293

1) 配列が引数としてメソッドまたは関数に渡される場合、それは参照渡しですか、それとも値渡しですか?

2) 配列を変数に代入するとき、新しい変数は元の配列への参照ですか、それとも新しいコピーですか?
これを行うのはどうですか:

$a = array(1,2,3);
$b = $a;

$bへの参照ですか$a

4

8 に答える 8

309

質問の 2 番目の部分については、(引用)と記載されているマニュアルの配列ページを参照してください。

配列の割り当てには、常に値のコピーが含まれます。参照演算子を使用して、参照によって配列をコピーします。

そして与えられた例:

<?php
$arr1 = array(2, 3);
$arr2 = $arr1;
$arr2[] = 4; // $arr2 is changed,
             // $arr1 is still array(2, 3)

$arr3 = &$arr1;
$arr3[] = 4; // now $arr1 and $arr3 are the same
?>


最初の部分については、試してみることをお勧めします ;-)

次のコード例を検討してください。

function my_func($a) {
    $a[] = 30;
}

$arr = array(10, 20);
my_func($arr);
var_dump($arr);

この出力が得られます:

array
  0 => int 10
  1 => int 20

これは、関数がパラメーターとして渡された「外部」配列を変更していないことを示しています。参照ではなく、コピーとして渡されます。

参照渡しが必要な場合は、次のように関数を変更する必要があります。

function my_func(& $a) {
    $a[] = 30;
}

出力は次のようになります。

array
  0 => int 10
  1 => int 20
  2 => int 30

今回は、配列が「参照渡し」されているためです。


マニュアルの参考文献の説明セクション を読むことを躊躇しないでください: それはあなたの質問のいくつかに答えるはずです ;-)

于 2010-01-08T21:34:57.613 に答える
146

最初の質問に関しては、呼び出しているメソッド/関数内で変更されない限り、配列は参照によって渡されます。メソッド/関数内で配列を変更しようとすると、最初に配列のコピーが作成され、次にコピーのみが変更されます。これにより、実際にはそうではないのに、配列が値渡しされているように見えます。

たとえば、この最初のケースでは、(パラメータ定義で & 文字を使用して) 参照によって $my_array を受け入れるように関数を定義していなくても、参照によって渡されます (つまり、メモリを浪費しません)。不要なコピーを含む)。

function handle_array($my_array) {  

    // ... read from but do not modify $my_array
    print_r($my_array);

    // ... $my_array effectively passed by reference since no copy is made
}

ただし、配列を変更すると、そのコピーが最初に作成されます (これにより、より多くのメモリが使用されますが、元の配列は影響を受けません)。

function handle_array($my_array) {

    // ... modify $my_array
    $my_array[] = "New value";

    // ... $my_array effectively passed by value since requires local copy
}

参考までに、これは「レイジー コピー」または「コピー オン ライト」として知られています。

于 2012-03-16T15:56:30.073 に答える
95

TL;DR

a) メソッド/関数は配列引数のみを読み取る=>暗黙的な (内部) 参照
b) メソッド/関数は配列引数を変更する=>
c) メソッド/関数の配列引数は参照として明示的にマークされている (アンパサンドを使用) =>明示的な (ユーザーランド) 参照

またはこれ:
-非アンパサンド配列 param : 参照によって渡されます。書き込み操作は、配列の新しいコピー、最初の書き込みで作成されたコピーを変更します。
-アンパサンド配列 param : 参照渡し。書き込み操作は元の配列を変更します。

覚えておいてください -アンパサンド以外の配列 param に書き込むと、 PHP は値のコピーを行います。というcopy-on-writeことです。この動作の C ソースを見せたいのですが、そこは怖いです。xdebug_debug_zval()を使用することをお勧めします。

パスカル・マルティンは正しかった。コスタ・コントスはさらにそうでした。

答え

場合によります。

ロングバージョン

これは自分のために書いていると思います。ブログか何かあればいいのに…

人々が参照 (またはポインター) について話すときはいつでも、通常、ロゴマシーに行き着きます (このスレッドを見てください!)。
PHPは由緒ある言語であるため、混乱を招く必要があると思いました(これは上記の回答の要約ですが)。なぜなら、2 人が同時に正しい場合もありますが、2 人の頭を合わせて 1 つの答えにしたほうがよいからです。

まず最初に、白黒で答えなければ衒学者ではないことを知っておく必要があります。物事は「はい/いいえ」よりも複雑です。

ご覧のとおり、値渡し/参照渡しの全体は、メソッド/関数スコープでその配列を使用して正確に何を行っているか、つまり読み取りまたは変更に非常に関連しています。

PHPは何と言っていますか?(別名「チェンジワイズ」)

マニュアルには次のように書かれています(強調は私のものです):

デフォルトでは、関数の引数は値で渡されます(そのため、関数内の引数の値が変更されても、関数の外部では変更されません)。関数がその引数を変更できるようにするには、それらを参照渡しする必要があります。

関数への引数が常に参照によって渡されるようにするには、関数定義で引数名の前にアンパサンド (&) を追加します。

私が知る限り、偉大で真面目な、神に正直なプログラマーが参照について話すとき、彼らは通常、その参照の値を変更することについて話します。そして、それはまさにマニュアルが語っていることです: hey, if you want to CHANGE the value in a function, consider that PHP's doing "pass-by-value".

ただし、彼らが言及していない別のケースがあります。何も変更しない場合はどうなりますか?ただ読むだけですか?
参照を明示的にマークしないメソッドに配列を渡し、関数スコープでその配列を変更しない場合はどうなるでしょうか? 例えば:

<?php
function readAndDoStuffWithAnArray($array) 
{
    return $array[0] + $array[1] + $array[2];
}

$x = array(1, 2, 3);

echo readAndDoStuffWithAnArray($x);

読んでください、私の仲間の旅行者。

PHPは実際に何をしますか?(別名「メモリワイズ」)

同じ大規模で真面目なプログラマーは、さらに真面目になると、参照に関する「メモリの最適化」について話します。PHPもそうです。だってPHP is a dynamic, loosely typed language, that uses copy-on-write and reference countingだから

巨大な配列をさまざまな関数に渡し、PHP がそれらのコピーを作成するのは理想的ではありません (結局のところ、これが「値渡し」が行うことです)。

<?php

// filling an array with 10000 elements of int 1
// let's say it grabs 3 mb from your RAM
$x = array_fill(0, 10000, 1); 

// pass by value, right? RIGHT?
function readArray($arr) { // <-- a new symbol (variable) gets created here
    echo count($arr); // let's just read the array
}

readArray($x);

さて、これが実際に値渡しであった場合、その配列のコピー が2 つあるため、3 MB 以上の RAM が失われることになりますよね?

違う。$arr変数を変更しない限り、それはメモリに関する参照です。あなたはそれを見ないだけです。これが、PHPが について話すときにユーザーランド参照に言及 し、内部参照と明示的 (アンパサンド付き) 参照を区別する理由です。 &$someVar

事実

そう、when an array is passed as an argument to a method or function is it passed by reference?

私は3 つ(ええ、3 つ) のケースを思いつきました:
a) メソッド/関数は配列引数のみを読み取り
ます b) メソッド/関数は配列引数を変更
します c) メソッド/関数配列引数は参照として明示的にマークされます (アンパサンド)


まず、その配列が実際に消費するメモリの量を見てみましょう (ここで実行):

<?php
$start_memory = memory_get_usage();
$x = array_fill(0, 10000, 1);
echo memory_get_usage() - $start_memory; // 1331840

その多くのバイト。偉大な。

a)メソッド/関数は配列引数のみを読み取ります

次に、上記の配列を引数として読み取るだけの関数を作成して、読み取りロジックに必要なメモリ量を確認します。

<?php

function printUsedMemory($arr) 
{
    $start_memory = memory_get_usage();

    count($arr);       // read
    $x = $arr[0];      // read (+ minor assignment)
    $arr[0] - $arr[1]; // read

    echo memory_get_usage() - $start_memory; // let's see the memory used whilst reading
}

$x = array_fill(0, 10000, 1); // this is 1331840 bytes
printUsedMemory($x);

推測したいですか?私は80を取得します!自分の目で確かめてください。これは、PHP マニュアルで省略されている部分です。パラメータが実際に値渡しされた場合、 bytes$arrに似たものが表示されます。1331840リファレンスのように$arr振る舞うようですね。それ参照であるためです-内部のものです。

b) メソッド/関数が配列引数を変更する

次に、そのパラメーターから読み取る代わりに、そのパラメーターに書き込みましょう。

<?php

function printUsedMemory($arr)
{
    $start_memory = memory_get_usage();

    $arr[0] = 1; // WRITE!

    echo memory_get_usage() - $start_memory; // let's see the memory used whilst reading
}

$x = array_fill(0, 10000, 1);
printUsedMemory($x);

繰り返しますが、自分の目で確かめてください。しかし、私にとっては、1331840 にかなり近い値です。したがって、この場合、配列実際に にコピーされてい$arrます。

c) メソッド/関数配列の引数が参照として明示的にマークされている (アンパサンドを使用)

ここで、明示的な参照への書き込み操作に必要なメモリ量を見てみましょう(ここで実行)。関数シグネチャのアンパサンドに注意してください。

<?php

function printUsedMemory(&$arr) // <----- explicit, user-land, pass-by-reference
{
    $start_memory = memory_get_usage();

    $arr[0] = 1; // WRITE!

    echo memory_get_usage() - $start_memory; // let's see the memory used whilst reading
}

$x = array_fill(0, 10000, 1);
printUsedMemory($x);

私の賭けは、あなたが最大200を獲得することです!したがって、これはアンパサンド以外の param から読み取るのとほぼ同じ量のメモリを消費します。

于 2014-04-08T14:22:46.077 に答える
5

配列が PHP のメソッドまたは関数に渡される場合、明示的に参照渡ししない限り、次のように値渡しされます。

function test(&$array) {
    $array['new'] = 'hey';
}

$a = $array(1,2,3);
// prints [0=>1,1=>2,2=>3]
var_dump($a);
test($a);
// prints [0=>1,1=>2,2=>3,'new'=>'hey']
var_dump($a);

2 番目の質問で$bは、 への参照ではなく$a、 のコピーです$a

最初の例と同じように、次のように$aして参照できます。

$a = array(1,2,3);
$b = &$a;
// prints [0=>1,1=>2,2=>3]
var_dump($b);
$b['new'] = 'hey';
// prints [0=>1,1=>2,2=>3,'new'=>'hey']
var_dump($a);
于 2010-01-08T21:36:34.147 に答える
1

このスレッドは少し古いですが、ここで私が遭遇したものがあります:

このコードを試してください:

$date = new DateTime();
$arr = ['date' => $date];

echo $date->format('Ymd') . '<br>';
mytest($arr);
echo $date->format('Ymd') . '<br>';

function mytest($params = []) {
    if (isset($params['date'])) {
        $params['date']->add(new DateInterval('P1D'));
    }
}

http://codepad.viper-7.com/gwPYMw

$params パラメーターには amp がなく、それでも $arr['date'] の値が変更されることに注意してください。これは、ここにある他のすべての説明や、これまでの私の考えと実際には一致しません。

$params['date'] オブジェクトを複製すると、2 番目に出力される日付は変わりません。文字列に設定しただけでは、出力にも影響しません。

于 2014-12-18T21:48:33.927 に答える