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

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

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

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

Is $b ссылка $a?

7 ответов


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

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

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

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

FYI-это известно как" ленивая копия "или"копирование при записи".


TL; DR

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

или такой:
- массив без амперсанда парам!--20-->: передается по ссылке; операции записи изменяют новую копию массива, копия которого создается при первой записи;
- амперсанд array param: передается по ссылке; операции записи изменяют исходный массив.

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

ответ

это зависит.

текст

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

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

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

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

что говорит PHP? (он же "перемены")

на руководство говорит это (Курсив мой):

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

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

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

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

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

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

Ну, если бы это действительно было pass-by-value, у нас было бы немного 3MB+ RAM, потому что есть два копии этого массив, верно?

неправильно. Пока мы не изменим $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

столько байт. Отличный.

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 ведет себя как ссылка, не так ли? Потому что это is a references - внутренний.

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. Итак, в этом случае массив is на самом деле копируется в $arr.

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

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

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