Являются ли массивы в PHP переданы по значению или по ссылке?

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

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

$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 

Поскольку на этот раз массив был передан «по ссылке».

Не стесняйтесь прочитать раздел « Ссылки», поясненный в руководстве: он должен ответить на некоторые из ваших вопросов 😉

Что касается вашего первого вопроса, массив передается по ссылке UNLESS, он изменяется внутри метода / функции, которую вы вызываете. Если вы попытаетесь изменить массив в методе / функции, сначала будет сделана его копия, а затем будет изменена только копия. Это заставляет кажется, что массив передается по значению, когда на самом деле это не так.

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

FYI – это называется «ленивая копия» или «копирование на запись».

TL; DR

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

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

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

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

Ответ

Это зависит.

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

Кажется, я пишу это для себя. У меня должен быть блог или еще что-то …

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

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

Как вы увидите, вся функция by-value / by-reference очень сильно связана с тем, что именно вы делаете с этим массивом в области методов / функций: чтение или изменение?

Что говорит PHP? (он же «по-разному»)

В руководстве говорится об этом (акцент мой):

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

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

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

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

 <?php function printArray($array) {} $x = array(1); printArray($x); 

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

Что делает PHP на самом деле? (иначе говоря, «память»)

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

Было бы нецелесообразно передавать HUGE массивы различным функциям, а PHP – копировать их (в конце концов, это значит «pass-by-value»):

 <?php // filling an array with 10000 elements of int 1 // let's say it grabs 3 mb from you 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 , чтобы различать внутренние и явные (с амперсандами).

факты

Итак, 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 

Это много байтов. Отлично.

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

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

 <?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 param был фактически передан по значению, вы увидите нечто похожее на 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 .

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

По умолчанию

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

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

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

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

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

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

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