PHP 中的数组在传递给函数时是否作为值或对新变量的引用进行复制?

发布于 2024-08-17 23:43:11 字数 199 浏览 12 评论 0原文

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

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

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

$b 是对 $a 的引用吗?

1) When an array is passed as an argument to a method or function, is it passed by reference, or by value?

2) When assigning an array to a variable, is the new variable a reference to the original array, or is it new copy?
What about doing this:

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

Is $b a reference to $a?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

溺孤伤于心 2024-08-24 23:43:11

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

数组赋值总是涉及值
复制。使用引用运算符
通过引用复制数组。

以及给定的示例:

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

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

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

For the second part of your question, see the array page of the manual, which states (quoting) :

Array assignment always involves value
copying. Use the reference operator to
copy an array by reference.

And the given example :

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

For the first part, the best way to be sure is to try ;-)

Consider this example of code :

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

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

It'll give this output :

array
  0 => int 10
  1 => int 20

Which indicates the function has not modified the "outside" array that was passed as a parameter : it's passed as a copy, and not a reference.

If you want it passed by reference, you'll have to modify the function, this way :

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

And the output will become :

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

As, this time, the array has been passed "by reference".

Don't hesitate to read the References Explained section of the manual : it should answer some of your questions ;-)

被翻牌 2024-08-24 23:43:11

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

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

仅供参考 - 这称为“惰性复制”或“写时复制”。

With regards to your first question, the array is passed by reference UNLESS it is modified within the method / function you're calling. If you attempt to modify the array within the method / function, a copy of it is made first, and then only the copy is modified. This makes it seem as if the array is passed by value when in actual fact it isn't.

For example, in this first case, even though you aren't defining your function to accept $my_array by reference (by using the & character in the parameter definition), it still gets passed by reference (ie: you don't waste memory with an unnecessary copy).

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
}

However if you modify the array, a copy of it is made first (which uses more memory but leaves your original array unaffected).

function handle_array($my_array) {

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

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

FYI - this is known as "lazy copy" or "copy-on-write".

静待花开 2024-08-24 23:43:11

TL;DR

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

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

请记住 - 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 实际上是做什么的? (又名“内存优化”)

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

将巨大的数组传递给各种函数以及 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 变量,它就是一个内存方面的引用。你只是看不到而已。这就是 PHP 提及用户的原因-land 在谈论 &$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!因此,这消耗的内存大约与从非&符号参数读取一样多。

TL;DR

a) the method/function only reads the array argument => implicit (internal) reference
b) the method/function modifies the array argument => value
c) the method/function array argument is explicitly marked as a reference (with an ampersand) => explicit (user-land) reference

Or this:
- non-ampersand array param: passed by reference; the writing operations alter a new copy of the array, copy which is created on the first write;
- ampersand array param: passed by reference; the writing operations alter the original array.

Remember - PHP does a value-copy the moment you write to the non-ampersand array param. That's what copy-on-write means. I'd love to show you the C source of this behaviour, but it's scary in there. Better use xdebug_debug_zval().

Pascal MARTIN was right. Kosta Kontos was even more so.

Answer

It depends.

Long version

I think I'm writing this down for myself. I should have a blog or something...

Whenever people talk of references (or pointers, for that matter), they usually end up in a logomachy (just look at this thread!).
PHP being a venerable language, I thought I should add up to the confusion (even though this a summary of the above answers). Because, although two people can be right at the same time, you're better off just cracking their heads together into one answer.

First off, you should know that you're not a pedant if you don't answer in a black-and-white manner. Things are more complicated than "yes/no".

As you will see, the whole by-value/by-reference thing is very much related to what exactly are you doing with that array in your method/function scope: reading it or modifying it?

What does PHP says? (aka "change-wise")

The manual says this (emphasis mine):

By default, function arguments are passed by value (so that if the
value of the argument within the function is changed, it does not get
changed outside of the function). To allow a function to modify its
arguments, they must be passed by reference.

To have an argument to a
function always passed by reference, prepend an ampersand (&) to the
argument name in the function definition

As far as I can tell, when big, serious, honest-to-God programmers talk about references, they usually talk about altering the value of that reference. And that's exactly what the manual talks about: hey, if you want to CHANGE the value in a function, consider that PHP's doing "pass-by-value".

There's another case that they don't mention, though: what if I don't change anything - just read?
What if you pass an array to a method which doesn't explicitly marks a reference, and we don't change that array in the function scope? E.g.:

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

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

echo readAndDoStuffWithAnArray($x);

Read on, my fellow traveller.

What does PHP actually do? (aka "memory-wise")

The same big and serious programmers, when they get even more serious, they talk about "memory optimizations" in regards to references. So does PHP. Because PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting, that's why.

It wouldn't be ideal to pass HUGE arrays to various functions, and PHP to make copies of them (that's what "pass-by-value" does, after all):

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

Well now, if this actually was pass-by-value, we'd have some 3mb+ RAM gone, because there are two copies of that array, right?

Wrong. As long as we don't change the $arr variable, that's a reference, memory-wise. You just don't see it. That's why PHP mentions user-land references when talking about &$someVar, to distinguish between internal and explicit (with ampersand) ones.

Facts

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

I came up with three (yeah, three) cases:
a) the method/function only reads the array argument
b) the method/function modifies the array argument
c) the method/function array argument is explicitly marked as a reference (with an ampersand)


Firstly, let's see how much memory that array actually eats (run here):

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

That many bytes. Great.

a) the method/function only reads the array argument

Now let's make a function which only reads the said array as an argument and we'll see how much memory the reading logic takes:

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

Wanna guess? I get 80! See for yourself. This is the part that the PHP manual omits. If the $arr param was actually passed-by-value, you'd see something similar to 1331840 bytes. It seems that $arr behaves like a reference, doesn't it? That's because it is a references - an internal one.

b) the method/function modifies the array argument

Now, let's write to that param, instead of reading from it:

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

Again, see for yourself, but, for me, that's pretty close to being 1331840. So in this case, the array is actually being copied to $arr.

c) the method/function array argument is explicitly marked as a reference (with an ampersand)

Now let's see how much memory a write operation to an explicit reference takes (run here) - note the ampersand in the function signature:

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

My bet is that you get 200 max! So this eats approximately as much memory as reading from a non-ampersand param.

油焖大侠 2024-08-24 23:43:11

默认情况下,

  1. 原语是按值传递的。与 Java 不同的是,字符串在 PHP 中是原始的。
  2. 原始数组通过值传递。
  3. 对象通过引用 传递。
  4. 对象数组通过值(数组)传递,但每个对象都是通过引用传递。

    <前><代码>field='world';

    $原始=数组($obj);

    函数示例($hello) {
    $hello[0]->field='mundo'; // 更改将应用​​于 $original
    $hello[1]=new stdClass(); // 更改不会应用于 $original
    $
    }

    示例($原始);

    var_dump($原始);
    // 数组(1) { [0]=>;对象(stdClass)#1 (1) { ["field"]=>字符串(5)“世界”}}

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

By default

  1. Primitives are passed by value. Unlikely to Java, string is primitive in PHP
  2. Arrays of primitives are passed by value
  3. Objects are passed by reference
  4. 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" } } 
    

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.

北渚 2024-08-24 23:43:11

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

When an array is passed to a method or function in PHP, it is passed by value unless you explicitly pass it by reference, like so:

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

In your second question, $b is not a reference to $a, but a copy of $a.

Much like the first example, you can reference $a by doing the following:

$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);
高速公鹿 2024-08-24 23:43:11

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

In PHP arrays are passed to functions by value by default, unless you explicitly pass them by reference, as the following snippet shows:

$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

Here is the output:

array(3) {
  [0]=>
  int(11)
  [1]=>
  int(22)
  [2]=>
  int(33)
}
array(3) {
  [0]=>
  int(66)
  [1]=>
  int(22)
  [2]=>
  int(33)
}
感情废物 2024-08-24 23:43:11

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

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

To extend one of the answers, also subarrays of multidimensional arrays are passed by value unless passed explicitely by reference.

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

The result is:

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)
}
旧人九事 2024-08-24 23:43:11

这个线程有点旧,但我刚刚遇到了一些东西:

尝试这个代码:

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

This thread is a bit older but here something I just came across:

Try this code:

$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

Note there is no amp for the $params parameter and still it changes the value of $arr['date']. This doesn't really match with all the other explanations here and what I thought until now.

If I clone the $params['date'] object, the 2nd outputted date stays the same. If I just set it to a string it doesn't effect the output either.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文