PHP中的数组是通过值还是通过引用传递的?

当一个数组作为parameter passing给一个方法或函数时,它是通过引用传递的?

那么做什么:

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

$b是对$b的引用吗?

对于问题的第二部分,请参阅手册的数组页面 ,其中指出(引用)

数组赋值始终涉及值复制。 使用引用运算符通过引用来复制数组。

而给出的例子:

 <?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 

这表明函数没有修改作为parameter passing的“外部”数组:它作为一个副本传递,而不是引用。

如果你想通过引用传递,你必须修改这个函数,这样:

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

输出将变成:

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

至于这次,这个数组已经被“通过引用”传递过来了。

不要犹豫,阅读手册的参考文献解释部分:它应该回答你的一些问题;-)

关于你的第一个问题,数组是通过引用传递的,除非在你调用的方法/函数中被修改。 如果您尝试在方法/函数中修改数组,则首先创build一个副本,然后只修改副本。 这使得它看起来好像数组是通过值传递的,但实际上并不是这样。

例如,在第一种情况下,即使你没有定义你的函数通过引用接受$ 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 } 

但是,如果修改数组,则会首先创build一个副本(使用更多内存,但不影响原始数组)。

 function handle_array($my_array) { // ... modify $my_array $my_array[] = "New value"; // ... $my_array effectively passed by value since requires local copy } 

仅供参考 – 这被称为“延迟复制”或“写入时复制”。

TL; DR

a)方法/函数只读取数组参数=> 隐式(内部)引用
b)方法/函数修改数组参数=>
c)方法/函数数组参数被显式标记为引用(带&符号)=> 显式(用户区)引用

或这个:
非&amp;数组参数 :通过引用传递; 写入操作会改变arrays的新副本,即在第一次写入时创build的副本;
&符号数组参数 :通过引用传递; 写入操作会改变原始数组。

请记住 – 在您写入非连接数组参数的那一刻, PHP会进行值复制。 这就是copy-on-write意思。 我很乐意向你展示这种行为的C源,但在那里很可怕。 最好使用xdebug_debug_zval() 。

帕斯卡尔·马丁是正确的。 Kosta Kontos更是如此。

回答

这取决于。

长版

我想我是为自己写的。 我应该有一个博客什么的…

每当人们谈论引用(或指针)时,他们通常会以logomachy结尾(只要看看这个线程 !)。
PHP是一个古老的语言,我想我应该加起来的混乱(即使这是上述答案的总结)。 因为,虽然两个人可以同时做对,但最好把他们的脑袋拼成一个答案。

首先, 如果你不以黑白的方式回答 ,你应该知道你不是一个学问 。 事情比“是/否”更复杂。

正如你将会看到的那样,整个by-value / by-reference的事情与你在方法/函数范围中对数组的操作有很大关系:读取或修改它?

PHP说什么? (又名“改变”)

手册说这(重点是我的):

默认情况下,函数参数是通过值传递的 (所以如果函数中参数的值被改变 ,它不会在函数之外被改变)。 要允许一个函数修改它的参数,它们必须通过引用传递

要让一个函数的参数始终通过引用传递,请在函数定义中的参数名称前加一个&符号(&)

据我所知,当大的,严肃的,诚实的上帝程序员谈论引用时,他们通常会谈论改变引用的价值 。 而这正是手册所说的: hey, if you want to CHANGE the value in a function, consider that PHP's doing "pass-by-value"

但还有另外一个例子,他们没有提到:如果我什么都不改变 – 只读?
如果将一个数组传递给一个没有显式标记引用的方法,而且我们不在函数作用域中更改该数组呢? 又名:

 <?php function printArray($array) {} $x = array(1); printArray($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 you 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); 

那么现在呢,如果这实际上是按值传递的,我们会有3MB + RAM,因为这个数组有两个副本,对吧?

错误。 只要我们不改变$arrvariables,这是一个参考, 记忆明智的 。 你只是没有看到它。 这就是为什么PHP在提及 &$someVar时提到 用户域引用的原因,以区分内部和外部(与&符号)。

事实

所以, when an array is passed as an argument to a method or function is it passed by reference?

我想出了三个 (耶,三个)情况:
a)方法/函数只读取数组参数
b)方法/函数修改数组参数
c)方法/函数数组参数显式标记为引用(带&号)


首先,让我们看看数组实际吃了多less内存(在这里运行):

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

那么多字节 大。

a)方法/函数只读取数组参数

现在我们来创build一个只读取所述数组作为参数的函数,我们将看到读取逻辑需要多less内存:

 <?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手册省略的部分。 如果$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)方法/函数数组参数显式标记为引用(带&号)

现在让我们来看看对一个显式引用的写入操作需要多less内存(在这里运行) – 注意函数签名中的和号:

 <?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最大! 所以这个和从一个非&amp;参数读取的内存大致相当。

默认

  1. 基元是通过价值传递的。 对于Java不太可能,string在PHP中是原始的
  2. 基元数组是按值传递的
  3. 对象通过引用传递
  4. 对象数组通过值(数组)传递,但每个对象都通过引用传递。

     <?php $obj=new stdClass(); $obj->field='world'; $original=array($obj); function example($hello) { $hello[0]->field='mundo'; // change will be applied in $original $hello[1]=new stdClass(); // change will not be applied in $original $ } example($original); var_dump($original); // array(1) { [0]=> object(stdClass)#1 (1) { ["field"]=> string(5) "mundo" } } 

注意:作为一个优化,每个值都被作为参考传递,直到它在函数内被修改。 如果它被修改,并且该值通过引用传递,那么它被复制并且副本被修改。

当数组传递给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); 

在第二个问题中, $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); 

这个线程是有点旧,但在这里我刚刚碰到的东西:

试试这个代码:

 $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参数没有放大器,仍然会改变$ arr ['date']的值。 这与其他所有的解释和我之前的想法都不符合。

如果我克隆$ params ['date']对象,第二个输出date保持不变。 如果我将它设置为一个string,它也不会影响输出。

在PHP中,数组默认情况下是按值传递给函数的,除非您明确地按照引用传递它们,如下面的代码片段所示:

 $foo = array(11, 22, 33); function hello($fooarg) { $fooarg[0] = 99; } function world(&$fooarg) { $fooarg[0] = 66; } hello($foo); var_dump($foo); // (original array not modified) array passed-by-value world($foo); var_dump($foo); // (original array modified) array passed-by-reference 

这是输出:

 array(3) { [0]=> int(11) [1]=> int(22) [2]=> int(33) } array(3) { [0]=> int(66) [1]=> int(22) [2]=> int(33) }