Вопрос

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

Как насчет того, чтобы сделать это:

$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

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


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

Другие советы

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

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

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

TL;DR

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

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

<?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 упоминания земля пользователя ссылки когда речь заходит о &$someVar, чтобы различать внутренние и явные (с амперсандом).

Факты

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

Я придумал три (да, три) случая:
а) метод/функция только читает аргумент массива
б) метод/функция изменяет аргумент массива
c) аргумент массива метода / функции явно помечен как ссылка (с помощью амперсанда).


Во-первых, давайте посмотрим, сколько памяти на самом деле потребляет этот массив (run здесь):

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

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

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

<?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)
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top