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



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



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



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


- Это $b ссылка $a?

728   7  

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

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

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

Паскаль Мартин был прав. Коста Контос был еще более удивлен.

ответ

это зависит.

текст

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

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

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

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

что говорит PHP? (ака "изменение мудрый")

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

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

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

насколько я могу судить, когда большие, серьезные, честные программисты говорят о ссылках, они обычно говорят о изменение значения этой ссылки. И это именно то, о чем говорится в руководстве: 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, это почему.

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


во-первых, давайте посмотрим, сколько памяти этот массив на самом деле ест (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 ведет себя как ссылка, не так ли? Это потому, что он и ссылки - внутренний.

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.

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

Comments

    Ничего не найдено.