PHP中的数组是作为值复制还是作为对新变量的引用复制,以及何时传递给函数?

1)当一个数组作为参数传递给一个方法或函数时,它是通过引用传递,还是通过值传递?

2)当分配一个数组到一个变量,新变量是一个原始数组的引用,还是它是新的副本?< br > 这样做怎么样:

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

$b$a的引用吗?

148066 次浏览

对于你的问题的第二部分,请参阅手册中的“Array”页,它声明(引用):

数组赋值总是包含值 复制。使用引用操作符to 通过引用复制数组

下面举个例子:

<?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
?>
< p > < br > 对于第一部分,最好的方法是尝试;-)

考虑下面的代码示例:

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

As,这一次,数组是“通过引用”传递的。

< p > < br > 不要犹豫,阅读手册的引用的解释部分:它应该回答你的一些问题;-)

当一个数组在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);

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

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

供你参考——这就是所谓的“惰性复制”或“写时复制”。

博士TL;

a)方法/函数只有读数组参数=> 隐式(内部)引用
b)方法/函数修改数组参数=> 价值
c)方法/函数数组参数被显式标记为引用(带有&号)=> 显式(用户-土地)引用

< p >或:< br > - 非&号数组参数:通过引用传递;写入操作更改数组的一个新副本,该副本是在第一次写入时创建的;
- &数组参数:通过引用传递;写入操作会改变原始数组。< / p >

记住,PHP对非&号数组参数进行值复制写作的那一刻。这就是copy-on-write的意思。我很乐意向你们展示这种行为的C源,但这很可怕。最好使用xdebug_debug_zval ()

帕斯卡·马丁是对的。科斯塔·康托斯更是如此。

回答

视情况而定。

长版本

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

每当人们谈论引用(或指针,就此而言)时,他们通常以一个logomachy结尾(看看这个线程!) PHP是一种受人尊敬的语言,我认为我应该对这些困惑进行总结(尽管这是对上述答案的总结)。因为,虽然两个人可能同时是对的,但你最好让他们一起思考出一个答案

首先,你应该知道如果你不以非黑即白的方式回答问题,你就不是书呆子。事情比“是/否”更复杂。

正如您将看到的,整个按值/按引用的事情与您在方法/函数作用域中对该数组所做的事情非常相关:读取它还是修改它?

PHP怎么说?(又名“change-wise”)

手册说(强调我的):

默认情况下,函数参数为通过值传递(因此如果 如果函数中实参的值是改变了,则不会获得 在函数外部更改)。允许函数修改 its 参数,它们必须是通过引用。< / p >

对a有一个参数 函数总是通过引用传递,在函数前加&号(&) 函数定义中的参数名称

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

不过,还有一种情况他们没有提到:如果我什么都不改变——只是阅读呢?< br > 如果您将一个数组传递给一个没有显式标记引用的方法,并且我们没有在函数作用域中更改该数组,该怎么办?例如:< / p >
<?php
function readAndDoStuffWithAnArray($array)
{
return $array[0] + $array[1] + $array[2];
}


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


echo readAndDoStuffWithAnArray($x);

读下去吧,我的旅伴。

PHP实际上是做什么的?(又名“memory-wise”)

同样是一些大而严肃的程序员,当他们更加严肃的时候,他们会谈论关于引用的“内存优化”。PHP也是如此。因为PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting为什么

将HUGE数组传递给各种函数,然后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);

好吧,现在,如果这实际上是值传递,我们将丢失一些3mb+的RAM,因为该数组有两个个副本,对吧?

错了。只要我们不改变$arr变量,它就是一个引用,memory-wise。你只是看不见而已。这就是为什么PHP 提到 < em >用户区域< / em >引用在谈论&$someVar时,以区分内部和显式(带有&号)。

事实

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

我想出了三个(是的,三个)情况:
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手册省略的部分。如果$arr参数实际上是按值传递的,你会看到类似1331840字节的东西。$arr看起来就像一个引用,不是吗?这是因为它 a引用-一个内部引用。

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块!所以它消耗的内存大约和读取非&参数一样多。

这个帖子有点老了,但这里有一些我刚刚发现的东西:

试试下面的代码:

$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']对象,第二个输出日期保持不变。如果我只是将它设置为一个字符串,它也不会影响输出。

在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)
}

默认情况下

  1. 原语是按值传递的。与Java不同,字符串在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" } }
    

Note: As an optimization, every single value is passed as reference until its modified inside the function. If it's modified and the value was passed by reference then, it's copied and the copy is modified.

为了扩展其中一个答案,多维数组的子数组也按值传递,除非显式地通过引用传递。

<?php
$foo = array( array(1,2,3), 22, 33);


function hello($fooarg) {
$fooarg[0][0] = 99;
}


function world(&$fooarg) {
$fooarg[0][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]=>
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
[1]=>
int(22)
[2]=>
int(33)
}
array(3) {
[0]=>
array(3) {
[0]=>
int(66)
[1]=>
int(2)
[2]=>
int(3)
}
[1]=>
int(22)
[2]=>
int(33)
}