Массивы в PHP передаются по значению или по ссылке? - PullRequest
238 голосов
/ 09 января 2010

Когда массив передается в качестве аргумента методу или функции, передается ли он по ссылке?

Как насчет этого:

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

Является ли $b ссылкой на $a

Ответы [ 8 ]

253 голосов
/ 09 января 2010

Что касается второй части вашего вопроса, см. Страницу руководства в массиве , в которой указано (цитирование) :

Присвоение массива всегда включает значение копирование. Используйте оператор ссылки для скопировать массив по ссылке.

И приведенный пример:

<?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 объясненный руководства: он должен ответить на некоторые ваши вопросы; -)

105 голосов
/ 16 марта 2012

Что касается вашего первого вопроса, массив передается по ссылке, ЕСЛИ МЕНЬШЕ не изменено в вызываемом вами методе / функции. Если вы пытаетесь изменить массив в методе / функции, сначала создается его копия, а затем изменяется только копия. Это создает впечатление, что массив передается по значению, хотя на самом деле это не так.

Например, в этом первом случае, даже если вы не определяете, что ваша функция принимает $ 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
}

К вашему сведению - это называется «отложенное копирование» или «копирование при записи».

70 голосов
/ 08 апреля 2014

TL; DR

a) метод / функция читает только аргумент массива => неявная (внутренняя) ссылка
б) метод / функция изменяет аргумент массива => значение
c) аргумент массива метода / функции явно помечается как ссылка (с амперсандом) => явная (пользовательская) ссылка

Или это:
- параметр массива без амперсанда : передается по ссылке; операции записи изменяют новую копию массива, копия которой создается при первой записи;
- параметр массива амперсанд : передается по ссылке; Операции записи изменяют исходный массив.

Помните - PHP делает копию значения в тот момент, когда вы пишете в параметре массива без амперсанда. Вот что означает copy-on-write. Я бы хотел показать вам источник C этого поведения, но там страшно. Лучше использовать xdebug_debug_zval () .

Паскаль МАРТИН был прав. Коста Контос был еще больше.

Ответ

Это зависит.

Длинная версия

Я думаю, что записываю это для себя. У меня должен быть блог или что-то ...

Всякий раз, когда люди говорят о ссылках (или указателях, если на то пошло), они обычно заканчивают логотипом (просто посмотрите на эту ветку !).
PHP был почтенным языком, и я подумал, что должен допустить путаницу (даже если это резюме приведенных выше ответов). Потому что, хотя два человека могут быть правы одновременно, вам лучше просто разбить им головы в один ответ.

Прежде всего, вы должны знать, что вы не педант, если не отвечаете черно-белым способом . Все сложнее, чем «да / нет».

Как вы увидите, вся вещь по значению / по ссылке очень сильно связана с тем, что именно вы делаете с этим массивом в области действия вашего метода / функции: его чтение или изменение?

Что говорит PHP? (он же "изменится")

В руководстве сказано следующее (выделено мной):

По умолчанию аргументы функции передаются по значению (так что если значение аргумента внутри функции изменено , не получается изменилось за пределами функции). Чтобы разрешить функции изменить ее аргументы, они должны быть переданы по ссылке .

Чтобы аргументировать функция всегда передается по ссылке, добавьте амперсанд (&) к имя аргумента в определении функции

Насколько я могу судить, когда большие, серьезные, честные программисты говорят о ссылках, они обычно говорят о изменении значения этой ссылки . И это именно то, о чем говорится в руководстве: hey, if you want to CHANGE the value in a function, consider that PHP's doing "pass-by-value".

Однако есть еще один случай, о котором они не упоминают: что, если я ничего не изменю - просто читаю?
Что если вы передадите массив методу, который явно не помечает ссылку, и мы не изменим этот массив в области действия функции? E.g.:

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

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

echo readAndDoStuffWithAnArray($x);

Продолжайте читать, мой попутчик.

Что на самом деле делает PHP? (он же "по памяти")

Те же самые большие и серьезные программисты, когда они становятся еще более серьезными, говорят об «оптимизации памяти» в отношении ссылок. Так же как и PHP. Потому что PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting, это , почему .

Не было бы идеальным передавать ОГРОМНЫЕ массивы различным функциям, а 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);

Ну, а теперь, если бы это было на самом деле передачей по значению, у нас бы ушло около 3 МБ + ОЗУ, потому что есть две копии этого массива, верно?

Неправильно. Пока мы не меняем переменную $arr, это ссылка в отношении памяти . Вы просто не видите этого. Вот почему PHP упоминает user-land ссылки , когда речь идет о &$someVar, чтобы различать внутренние и явные (с амперсандом).

Факты

Итак, when an array is passed as an argument to a method or function is it passed by reference?

Я придумал три (да, три) дела:
а) метод / функция читает только аргумент массива
б) метод / функция изменяет аргумент массива
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.

в) аргумент массива метода / функции явно помечается как ссылка (с амперсандом)

Теперь давайте посмотрим, сколько памяти занимает операция записи в явную ссылку (запустите здесь ) - обратите внимание на амперсанд в сигнатуре функции:

<?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! Таким образом, это потребляет примерно столько же памяти, сколько при чтении из параметра без амперсанда .

11 голосов
/ 02 апреля 2016

По умолчанию

  1. Примитивы передаются по значению. В отличие от Java, строка является примитивной в PHP
  2. Массивы примитивов передаются по значению
  3. Объекты передаются по ссылке
  4. Массивы объектов передаются по значению (массиву), но каждый объект передается по ссылке.

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

Примечание. В качестве оптимизации каждое отдельное значение передается в качестве ссылки, пока оно не будет изменено внутри функции. Если оно было изменено и значение было передано по ссылке, оно копируется и копия изменяется.

4 голосов
/ 09 января 2010

Когда массив передается методу или функции в 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);
1 голос
/ 19 декабря 2014

Эта тема немного старше, но здесь я только что натолкнулся:

Попробуйте этот код:

$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'], вторая выведенная дата остается прежней. Если я просто установлю его в строку, это не повлияет на вывод.

0 голосов
/ 15 мая 2019

Чтобы расширить один из ответов, также подмассивы многомерных массивов передаются по значению, если явно не переданы по ссылке.

<?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)
}
0 голосов
/ 01 февраля 2015

В 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)
}
...