首页 文章

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

提问于
浏览
218

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

这样做怎么样:

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

$b 是对 $a 的引用吗?

7 回答

  • 62

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

    这个帖子有点老了但是我遇到过这样的事情:

    试试这段代码:

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

  • 3

    对于问题的第二部分,请参阅array page of the manual,其中说明(引用):

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

    给出的例子:

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

    因为,这一次,数组已经“通过引用”传递 .

    不要犹豫,阅读手册的References Explained部分:它应该回答你的一些问题;-)

  • 0

    TL; DR

    a)方法/函数 only reads 数组参数=> implicit (internal) reference
    b)方法/函数 modifies 数组参数=> value
    c)方法/函数数组参数显式标记为引用(带符号)=> explicit (user-land) reference

    或这个:

    • non-ampersand array param :通过引用传递;写操作改变了数组的新副本,在第一次写入时创建的副本;
    • ampersand array param :通过引用传递;写入操作改变了原始数组 .

    记住 - 当你写入非&符号数组参数时,PHP会进行值复制 . 这就是 copy-on-write 的意思 . 我在那里吓人 . 更好用xdebug_debug_zval() .

    帕斯卡马丁是对的 . Kosta Kontos更是如此 .

    答案

    这取决于 .

    长版

    我想我正在为自己写这篇文章 . 我应该有博客或其他东西......

    每当人们谈论引用(或指针,就此问题)时,他们通常会陷入一种逻辑上(只需看看thread!) .
    PHP是一种古老的语言,我认为我应该加入混乱(尽管这是对上述答案的总结) . 因为,尽管两个人可以同时做对,但你最好只是将他们的头脑拼凑成一个答案 .

    首先,你应该知道你是以黑白方式回答的 . 事情比"yes/no"更复杂 .

    正如您将看到的那样,整个按值/按引用的内容与您在方法/函数范围内使用该数组的确切内容非常相关:读取它还是修改它?

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

    manual说这个(强调我的):

    默认情况下,函数参数按值传递(因此,如果函数中的参数值发生更改,则不会在函数外部更改) . 要允许函数修改其参数,必须通过引用传递它们 . 要使函数的参数始终通过引用传递,请在函数定义中为参数名称添加一个与号(&)

    据我所知,当大,严肃,诚实的上帝程序员谈论引用时,他们通常会谈论改变该引用的 Value . 而这正是手册所说的: 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实际上做了什么? (又名“记忆”)

    相同的大而严肃的程序员,当他们变得更加严肃时,他们会在引用方面谈论"memory optimizations" . PHP也是如此 . 因为 PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting ,那是why .

    将HUGE数组传递给各种函数并不是理想的,并且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内存消失,因为该数组有两个副本,对吧?

    错误 . 只要我们不更改 $arr 变量,那就是参考, memory-wise . 你只是在谈论时为什么要这么做 &$someVar ,区分内部和显式(带&符号) .

    事实

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

    我想出了 three (是的,三个)案例:
    a)方法/函数 only reads 数组参数
    b)方法/函数 modifies 数组参数
    c)方法/函数数组参数被明确标记为引用(带符号)


    首先,让我们看一下数组实际吃多少内存(运行here):

    <?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岁了! See for yourself . 这是PHP手册省略的部分 . 如果 $arr param实际上是按值传递的,那么你会看到类似于 1331840 字节的内容 . 似乎 $arr 的行为就像一个参考,因为它是一个引用 - 一个内部引用 .

    b)方法/函数修改数组参数

    现在,让我们 write 到那个参数,而不是从它读取:

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

    再次,see for yourself,但是,对我来说,这非常接近1331840.所以在这种情况下,数组 is 实际上被复制到 $arr .

    c)方法/函数数组参数明确标记为引用(带符号)

    现在让我们看一下显式引用的写操作占用多少内存(运行here) - 注意函数签名中的&符号:

    <?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分!因此,这会占用与 reading from a non-ampersand param 一样多的内存 .

  • 9

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

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

    仅供参考 - 这被称为“懒惰副本”或“写时复制” .

  • 1

    默认情况下

    • 基元按值传递 . 与Java不同,字符串在PHP中是原始的

    • 基元的数组按值传递

    • 对象由 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" } }
    

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

  • 96

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

相关问题