TL; DR
a) metoda / funkcja odczytuje tylko argument tablicy => niejawne (wewnętrzne) odniesienie
b) metoda / funkcja modyfikuje argument tablicy => wartość
c) argument metody / funkcji tablicy jest wyraźnie oznaczony jako odwołanie (ze znakiem ampersand) => wyraźne odniesienie (user-land)
Lub to:
- parametr niezamienny i tablicowy : przekazany przez referencję; operacje zapisu zmieniają nową kopię tablicy, która jest tworzona przy pierwszym zapisie;
- parametr ampersand array : przekazany przez odniesienie; operacje zapisu zmieniają oryginalną tablicę.
Pamiętaj - PHP wykonuje kopiowanie wartości w momencie, gdy piszesz do parametru nie będącego znakiem ampersand. To copy-on-write
znaczy. Chciałbym pokazać Ci źródło tego zachowania, ale tam jest przerażające. Lepiej użyj xdebug_debug_zval () .
Pascal MARTIN miał rację. Kosta Kontos był jeszcze bardziej.
Odpowiedź
To zależy.
Długa wersja
Myślę, że zapisuję to dla siebie. Powinienem mieć blog czy coś ...
Ilekroć ludzie mówią o referencjach (lub wskaźnikach, jeśli o to chodzi), zwykle kończą się logomatyką (wystarczy spojrzeć na ten wątek !).
PHP jako czcigodny język, pomyślałem, że powinienem dodać do zamieszania (choć jest to streszczenie powyższych odpowiedzi). Ponieważ chociaż dwie osoby mogą mieć rację w tym samym czasie, lepiej po prostu połamać sobie głowy w jedną odpowiedź.
Po pierwsze, powinieneś wiedzieć, że nie jesteś pedantem, jeśli nie odpowiadasz w czarno-biały sposób . Sprawy są bardziej skomplikowane niż „tak / nie”.
Jak zobaczysz, cała rzecz według wartości / referencji jest bardzo związana z tym, co dokładnie robisz z tą tablicą w zakresie metody / funkcji: czy ją czy modyfikujesz?
Co mówi PHP? (alias „zmiany”)
Instrukcja mówi ten (mój nacisk):
Domyślnie argumenty funkcji są przekazywane przez wartość (tak, że jeśli wartość argumentu w funkcji zostanie zmieniona , nie zostanie zmieniona poza funkcją). Aby umożliwić funkcji modyfikowanie jej argumentów, muszą zostać przekazane przez odwołanie .
Aby argument funkcji był zawsze przekazywany przez odwołanie, wstaw znak ampersand (&) do nazwy argumentu w definicji funkcji
O ile mi wiadomo, kiedy duzi, poważni, uczciwi programiści mówią o referencjach, zwykle mówią o zmianie wartości tych referencji . I to jest dokładnie to, co mówi o ręcznych: hey, if you want to CHANGE the value in a function, consider that PHP's doing "pass-by-value"
.
Jest jednak inny przypadek, o którym nie wspominają: co jeśli nic nie zmienię - po prostu przeczytaj?
Co się stanie, jeśli przekażesz tablicę metodzie, która nie oznacza jawnie odwołania, a my nie zmienimy tej tablicy w zakresie funkcji? Na przykład:
<?php
function readAndDoStuffWithAnArray($array)
{
return $array[0] + $array[1] + $array[2];
}
$x = array(1, 2, 3);
echo readAndDoStuffWithAnArray($x);
Czytaj dalej, mój towarzyszu podróży.
Co właściwie robi PHP? (aka „pod względem pamięci”)
Ci sami duzi i poważni programiści, kiedy stają się jeszcze poważniejsi, mówią o „optymalizacjach pamięci” w odniesieniu do referencji. Podobnie PHP. Ponieważ PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting
właśnie dlatego .
Nie byłoby idealne przekazywanie OGROMNYCH tablic do różnych funkcji, a PHP tworzenie ich kopii (w końcu to właśnie robi funkcja „pass-by-value”):
<?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);
Cóż, teraz, gdyby to była wartość przekazywana, nie byłoby już około 3 MB pamięci RAM, ponieważ istnieją dwie kopie tej tablicy, prawda?
Źle. Dopóki nie zmienimy $arr
zmiennej, jest to odniesienie pod względem pamięci . Po prostu tego nie widzisz. Właśnie dlatego PHP wspomina o odniesieniach do ziemi użytkownika podczas &$someVar
rozróżniania wewnętrznych i jawnych (ze znakami ampersand).
Fakty
Więc, when an array is passed as an argument to a method or function is it passed by reference?
Wymyśliłem trzy (tak, trzy) przypadki:
a) metoda / funkcja odczytuje tylko argument tablicy;
b) metoda / funkcja modyfikuje argument tablicy;
c) argument metody / funkcji tablicy jest wyraźnie oznaczony jako odwołanie (za pomocą ampersand)
Po pierwsze, zobaczmy, ile pamięci faktycznie zjada tablica (uruchom tutaj ):
<?php
$start_memory = memory_get_usage();
$x = array_fill(0, 10000, 1);
echo memory_get_usage() - $start_memory; // 1331840
Tyle bajtów. Wspaniały.
a) metoda / funkcja odczytuje tylko argument tablicy
Stwórzmy teraz funkcję, która odczytuje tylko tę tablicę jako argument i zobaczymy, ile pamięci zajmuje logika odczytu:
<?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);
Chcesz zgadnąć? Dostaję 80! Przekonaj się sam . Jest to część, którą omija podręcznik PHP. Jeśli $arr
parametr rzeczywiście był przekazywany przez wartość, zobaczysz coś podobnego do 1331840
bajtów. Wygląda na to, że $arr
zachowuje się jak odniesienie, prawda? To dlatego, że jest to odniesienie - wewnętrzne.
b) metoda / funkcja modyfikuje argument tablicy
Teraz, niech pisze na tym param, zamiast czytać z niego:
<?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);
Ponownie, przekonaj się sam , ale dla mnie jest to bardzo bliskie bycia 1331840. W tym przypadku tablica jest kopiowana $arr
.
c) argument tablicy metod / funkcji jest wyraźnie oznaczony jako odwołanie (ze znakiem ampersand)
Zobaczmy teraz, ile pamięci zajmuje operacja zapisu do jawnego odwołania (uruchom tutaj ) - zwróć uwagę na znak ampersand w podpisie funkcji:
<?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);
Założę się, że otrzymasz maksymalnie 200! Więc zjada to w przybliżeniu tyle samo pamięci, co odczyt z paramperów i ampers .