PHP 中的数组是作为值复制还是作为对新变量的引用以及何时传递给函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2030906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are arrays in PHP copied as value or as reference to new variables, and when passed to functions?
提问by Frank
1) When an array is passed as an argument to a method or function, is it passed by reference, or by value?
1) 当数组作为参数传递给方法或函数时,它是按引用传递还是按值传递?
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:
2) 将数组赋值给变量时,新变量是对原始数组的引用,还是新副本?
这样做怎么样:
$a = array(1,2,3);
$b = $a;
Is $ba reference to $a?
是$b参考$a吗?
回答by Pascal MARTIN
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 Explainedsection of the manual : it should answer some of your questions ;-)
不要犹豫,阅读手册的参考文献解释部分:它应该回答你的一些问题;-)
回答by Kosta Kontos
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).
例如,在第一种情况下,即使您没有将函数定义为通过引用接受 $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
}
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".
仅供参考 - 这被称为“懒惰复制”或“写时复制”。
回答by nevvermind
TL;DR
TL; 博士
a) the method/function only readsthe array argument => implicit (internal) reference
b) the method/function modifiesthe array argument => value
c) the method/function array argument is explicitly marked as a reference (with an ampersand) => explicit (user-land) reference
a) 方法/函数只读取数组参数 =>隐式(内部)引用
b) 方法/函数修改数组参数 =>值
c) 方法/函数数组参数被显式标记为引用(带有 & 符号) =>显式(用户空间)参考
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 writeto the non-ampersand array param. That's what copy-on-writemeans. I'd love to show you the C source of this behaviour, but it's scary in there. Better use xdebug_debug_zval().
请记住 - PHP 会在您写入非与号数组参数的那一刻进行值复制。就是这个copy-on-write意思。我很想向您展示这种行为的 C 源代码,但它在那里很可怕。更好地使用xdebug_debug_zval()。
Pascal MARTIN was right. Kosta Kontos was even more so.
帕斯卡·马丁是对的。Kosta Kontos 更是如此。
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.
每当人们谈论引用(或指针,就此而言)时,他们通常会陷入一个logomachy(看看这个线程!)。
PHP 是一种古老的语言,我认为我应该增加混淆(即使这是上述答案的摘要)。因为,尽管两个人可能同时是对的,但最好还是将他们的脑袋凑成一个答案。
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")
PHP 说什么?(又名“改变明智”)
The manualsays 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 modifyits 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".
据我所知,当大的、严肃的、对上帝诚实的程序员谈论引用时,他们通常谈论改变引用的值。这正是手册所说的: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")
PHP实际上是做什么的?(又名“记忆力”)
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.
同样大而认真的程序员,当他们变得更加认真时,他们会谈论关于引用的“内存优化”。PHP 也是如此。因为PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting,这就是为什么。
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 来制作它们的副本并不理想(毕竟,这就是“按值传递”所做的):
<?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 twocopies of that array, right?
那么现在,如果这实际上是按值传递,我们将有一些 3mb+ RAM 消失,因为该数组有两个副本,对吗?
Wrong. As long as we don't change the $arrvariable, that's a reference, memory-wise. You just don't see it. That's why PHP mentionsuser-landreferenceswhen talking about &$someVar, to distinguish between internal and explicit (with ampersand) ones.
错误的。只要我们不更改$arr变量,这就是一个引用,内存方面。你只是没有看到。这就是 PHP在谈论,时提到用户空间引用的原因&$someVar,以区分内部引用和显式(带有 & 符号)引用。
Facts
事实
So, when an array is passed as an argument to a method or function is it passed by reference?
所以, 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 readsthe array argument
b) the method/function modifiesthe array argument
c) the method/function array argument is explicitly marked as a reference (with an ampersand)
我想出了三个(是的,三个)情况:
a)方法/函数只读取数组参数
b)方法/函数修改数组参数
c)方法/函数数组参数被显式标记为引用(带有&符号)
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 readsthe array argument
a) 方法/函数只读取数组参数
Now let's make a function which only readsthe 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 $arrparam was actually passed-by-value, you'd see something similar to 1331840bytes. It seems that $arrbehaves like a reference, doesn't it? That's because it isa references - an internal one.
想猜吗?我得80!自己看看。这是PHP手册省略的部分。如果$arr参数实际上是按值传递的,您会看到类似于1331840字节的内容。似乎它的$arr行为就像一个参考,不是吗?那是因为它是一个引用——一个内部引用。
b) the method/function modifiesthe array argument
b) 方法/函数修改数组参数
Now, let's writeto 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 isactually being copied to $arr.
再次,看到自己,但对我来说,那是相当接近在这种情况下是1331840.因此,该阵列是实际上被复制到$arr。
c) the method/function array argument is explicitly marked as a reference (with an ampersand)
c) 方法/函数数组参数被显式标记为引用(带有 & 号)
Now let's see how much memory a write operation to an explicit referencetakes (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.
我敢打赌,您最多可以获得 200 个!因此,这与读取非 & 符号 param 所消耗的内存大致相同。
回答by magallanes
By default
默认情况下
- 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" } }
- 原语是按值传递的。与 Java 不同,字符串在 PHP 中是原始的
- 基元数组按值传递
- 对象通过引用传递
对象数组是按值(数组)传递的,但每个对象都是按引用传递的。
<?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.
注意:作为优化,每个值都作为引用传递,直到它在函数内部被修改。如果它被修改并且值是通过引用传递的,那么它被复制并且副本被修改。
回答by Corey Ballou
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:
当数组传递给 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);
In your second question, $bis not a reference to $a, but a copy of $a.
在您的第二个问题中,$b不是对 的引用$a,而是$a.
Much like the first example, you can reference $aby doing the following:
与第一个示例非常相似,您可以$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);
回答by K.Karamazen
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)
}
回答by robbash
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
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.
请注意,$params 参数没有放大器,但它仍然会更改 $arr['date'] 的值。这与这里的所有其他解释以及我迄今为止的想法并不真正相符。
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.
如果我克隆 $params['date'] 对象,第二个输出日期保持不变。如果我只是将它设置为一个字符串,它也不会影响输出。
回答by John Sonderson
In PHP arrays are passed to functions by value by default, unless you explicitly pass them by reference, as the following snippet shows:
在 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
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)
}

