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

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

Как это сделать:

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

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

Ответы

Ответ 1

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

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

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

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

Как, на этот раз, массив был передан "по ссылке".


Не стесняйтесь читать раздел Справочник по объяснению в руководстве: он должен ответить на некоторые из ваших вопросов; -)

Ответ 2

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

Например, в этом первом случае, даже если вы не определяете свою функцию для приема $my_array по ссылке (с помощью символа и символа в определении параметра), он по-прежнему передается по ссылке (т.е. t удалять память с ненужной копией).

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 - это называется "ленивая копия" или "копирование на запись".

Ответ 3

TL; DR

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

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

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

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

Ответ

Это зависит.

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

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

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

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

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

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

Руководство говорит это (выделение мое):

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

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

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

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

<?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, вот почему.

Не было бы идеальным передавать массивы HUGE различным функциям, а PHP делать их копии (в конце концов, именно это и делает "передача по значению"):

<?php

// filling an array with 10000 elements of int 1
// let 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 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 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 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 see the memory used whilst reading
}

$x = array_fill(0, 10000, 1);
printUsedMemory($x);

Моя ставка в том, что вы получите 200 макс! Так что это съедает примерно столько же памяти, сколько и чтение из параметра без амперсанда.

Ответ 4

По умолчанию

  • Примитивы передаются по значению. Вряд ли 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" } } 
    

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

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