1)当一个数组作为参数传递给一个方法或函数时,它是通过引用传递,还是通过值传递?
2)将数组赋值给变量时,新变量是对原始数组的引用,还是新复制? 这样做怎么样:
$a = array(1,2,3);
$b = $a;
b是a的引用吗?
1)当一个数组作为参数传递给一个方法或函数时,它是通过引用传递,还是通过值传递?
2)将数组赋值给变量时,新变量是对原始数组的引用,还是新复制? 这样做怎么样:
$a = array(1,2,3);
$b = $a;
b是a的引用吗?
当前回答
博士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块!所以这消耗的内存大约和读取非&参数一样多。
其他回答
博士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块!所以这消耗的内存大约和读取非&参数一样多。
为了扩展其中一个答案,多维数组的子数组也按值传递,除非显式地通过引用传递。
<?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 通过引用复制数组。
下面举个例子:
<?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中,数组默认情况下是通过值传递给函数的,除非你显式地通过引用传递它们,如下面的代码片段所示:
$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)
}
这个帖子有点老了,但这里有一些我刚刚发现的东西:
试试下面的代码:
$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']对象,第二个输出日期保持不变。如果我只是将它设置为一个字符串,它也不会影响输出。