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

2)将数组赋值给变量时,新变量是对原始数组的引用,还是新复制? 这样做怎么样:

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

b是a的引用吗?


关于你的问题的第二部分,请参阅手册的数组页,其中声明(引用):

数组赋值总是涉及到值 复制。使用引用操作符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
?>

对于第一部分,最好的方法是尝试;-)

考虑下面的代码示例:

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,这一次,数组是“通过引用”传递的。

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

当一个数组在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)方法/函数数组参数被显式标记为引用(带有&号)=>显式(user-land)引用

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

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

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

回答

视情况而定。

长版本

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

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

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

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

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

手册上是这样说的(强调的是我的):

默认情况下,函数参数是按值传递的(因此如果 函数内参数的值被改变,它不会得到 在函数外部更改)。允许函数修改其 参数必须通过引用传递。 对a进行论证 函数总是通过引用传递,则在 函数定义中的参数名称

据我所知,当大型、严肃、诚实的程序员谈论引用时,他们通常会谈论改变引用的价值。这正是手册所讲的:嘿,如果你想改变一个函数的值,考虑一下PHP在做“值传递”。

不过,还有一种情况他们没有提到:如果我什么都不改变——只是阅读呢? 如果您将一个数组传递给一个没有显式标记引用的方法,并且我们没有在函数作用域中更改该数组,该怎么办?例如:

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

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

echo readAndDoStuffWithAnArray($x);

读下去吧,我的旅伴。

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

同样是一些大而严肃的程序员,当他们更加严肃的时候,他们会谈论关于引用的“内存优化”。PHP也是如此。因为PHP是一种动态的、松散类型的语言,使用写时复制和引用计数,这就是原因所在。

将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以上的内存,因为数组有两个副本?

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

事实

那么,当数组作为参数传递给方法或函数时,它是通过引用传递的吗?

我提出了三个(是的,三个)案例: 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看起来就像一个引用,不是吗?这是因为它是一个内部引用。

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

默认情况下

Primitives are passed by value. Unlikely to Java, string is primitive in PHP Arrays of primitives are passed by value Objects are passed by reference Arrays of objects are passed by value (the array) but each object is passed by reference. <?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
$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)
}