1)当将数组作为参数传递给一个方法或函数时,它是按引用传递还是按值传递?
2)将数组分配给一个变量时,新变量是指向原始数组的引用,还是一个新的副本?
进行以下操作呢:
$a = array(1,2,3);
$b = $a;
$b
是不是指向 $a
的引用?
1)当将数组作为参数传递给一个方法或函数时,它是按引用传递还是按值传递?
2)将数组分配给一个变量时,新变量是指向原始数组的引用,还是一个新的副本?
进行以下操作呢:
$a = array(1,2,3);
$b = $a;
$b
是不是指向 $a
的引用?
关于第二个问题,请参见手册中的数组页面,其中指出:(引用)
数组赋值总是涉及值复制。 使用引用操作符通过引用复制数组。
给出了以下示例:
<?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
因为这次数组是“按引用传递”的,所以……
不要犹豫阅读手册中的引用解释部分:它应该可以回答你的一些问题;-)
&
,所以应该 - 请参见http://php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign - Pascal MARTIN关于您的第一个问题,如果在调用的方法/函数内修改数组,则会传递引用。如果尝试在该方法/函数内修改数组,则首先会创建副本,然后只修改副本。这使得数组似乎是按值传递,但实际上不是。
例如,在此第一种情况下,即使您没有定义函数通过引用接受$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
}
顺便提一下 - 这被称为“懒复制”或“写时复制”。
a) 方法/函数仅读取数组参数 => 隐式(内部)引用
b) 方法/函数修改数组参数 => 值
c) 方法/函数数组参数明确标记为引用(带有&符号) => 显式(用户级)引用
或者这样:
- 非&符号数组参数: 通过引用传递; 写入操作会更改新的数组副本,该副本在第一次写入时创建;
- &符号数组参数: 通过引用传递; 写入操作会更改原始数组。
请记住 - PHP在您写入非&符号数组参数时立即进行值复制。这就是copy-on-write
的含义。我很想向您展示此行为的C源代码,但里面太可怕了。最好使用xdebug_debug_zval()。
Pascal MARTIN是正确的。Kosta Kontos更加正确。
这要看情况。
我想我是在为自己写下这些。我应该有一个博客之类的东西...
每当人们谈论引用(或指针),他们通常会陷入争辩中(只需看看这个thread!)。
PHP是一种古老的语言,我想我应该增加混乱(尽管这是上述答案的摘要)。因为,尽管两个人可能同时正确,但最好将他们的头合并成一个答案。
首先,你应该知道如果你不以黑白方式回答,你就不是一个学究。事情比“是/否”更加复杂。
正如你将看到的,整个按值/按引用的问题与你在方法/函数范围内对该数组做了什么密切相关:读取它还是修改它?
手册中这样说(重点是我的):
默认情况下,函数参数是按值传递的(因此,如果函数内部参数的值更改,它不会在函数外部更改)。要允许函数修改其参数,它们必须通过引用传递。
要求函数始终通过引用传递参数,请在函数定义中的参数名称前加上&符号。
据我所知,当大型、严肃、诚实可靠的程序员谈论引用时,他们通常谈论更改该引用的值。这正是手册所讲述的:嘿,如果你想在函数中改变一个值,请考虑PHP正在执行“按值传递”
。
虽然他们没有提到另一种情况: 如果我不做出任何更改 - 只是阅读呢?
如果您将一个数组传递给一个没有明确标记引用的方法,并且我们在函数范围内不更改该数组,会发生什么?例如:
<?php
function readAndDoStuffWithAnArray($array)
{
return $array[0] + $array[1] + $array[2];
}
$x = array(1, 2, 3);
echo readAndDoStuffWithAnArray($x);
请继续阅读,我的旅行者同伴。
同样那些专业的程序员们,当他们变得更加严谨时,他们会谈论关于引用的“内存优化”。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在谈论&$someVar时mentions用户空间引用,以区分内部和显式(带有&符号)引用。
所以,当一个数组作为参数传递给方法或函数时,它是按引用传递的吗?
我想到了三种情况:
a) 方法/函数仅读取数组参数
b) 方法/函数修改数组参数
c) 方法/函数数组参数被明确标记为引用(使用&符号)
<?php
$start_memory = memory_get_usage();
$x = array_fill(0, 10000, 1);
echo memory_get_usage() - $start_memory; // 1331840
那么多字节。太好了。
现在让我们编写一个函数,它将数组作为参数仅读取,然后我们将看到读取逻辑所占用的内存量:
<?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
的行为像一个引用,不是吗?那是因为它是一个内部引用。
现在,让我们写入该参数,而不是从中读取:
<?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
中。
现在让我们看一下显式引用的写操作需要多少内存(在此处运行)-请注意函数签名中的&:
<?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);
默认情况下:
对象数组按值(数组本身)传递,但每个对象都按引用传递。
<?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中将数组传递给方法或函数时,如果您没有显式地通过引用传递它,它将被按值传递,如下所示:
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);
在 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)
}
<?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)
}
$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']的值。这与此处的所有其他解释以及我之前的想法并不完全匹配。