São matrizes em PHP passados ??por valor ou por referência?
-
19-09-2019 - |
Pergunta
Quando uma matriz é transmitida como um argumento para um método ou função é que passado por referência?
O que fazer sobre isso:
$a = array(1,2,3);
$b = $a;
é $b
uma referência a $a
?
Solução
Para a segunda parte da sua pergunta, consulte o href="http://php.net/manual/en/language.types.array.php" rel="noreferrer"> página variedade , que estados (citando) :
a atribuição sempre envolve valor copiando. Usar o operador referência a copiar uma matriz por referência.
E o exemplo dado:
<?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
?>
Para a primeira parte, a melhor maneira de ter certeza é tentar; -)
Veja este exemplo de código:
function my_func($a) {
$a[] = 30;
}
$arr = array(10, 20);
my_func($arr);
var_dump($arr);
Ele vai dar essa saída:
array
0 => int 10
1 => int 20
O que indica que a função não modificou a matriz "de fora" que foi passado como um parâmetro:. É passada como uma cópia, e não uma referência
Se você quer que ele passou por referência, você vai ter que modificar a função, da seguinte maneira:
function my_func(& $a) {
$a[] = 30;
}
E a saída será:
array
0 => int 10
1 => int 20
2 => int 30
Como, desta vez, a matriz foi passada "por referência".
Não hesite em ler as Referências explicado seção do manual: deve responder algumas de suas perguntas; -)
Outras dicas
Com relação à sua primeira pergunta, a matriz é transmitida por referência a menos que seja modificado dentro do método / função que você está chamando. Se você tentar modificar a matriz dentro do método / função, uma cópia do que é feito em primeiro lugar, e, em seguida, apenas a cópia é modificada. Isso faz parecer como se a matriz é passado por valor quando na realidade não é.
Por exemplo, neste primeiro caso, mesmo que você não está definindo sua função para aceitar $ my_array por referência (utilizando o caractere & na definição do parâmetro), ele ainda é passado por referência (ou seja: você não' t memória resíduos com uma cópia desnecessário).
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
}
No entanto, se você modificar a matriz, uma cópia do que é feito primeiro (que usa mais memória, mas deixa a sua matriz original inalterado).
function handle_array($my_array) {
// ... modify $my_array
$my_array[] = "New value";
// ... $my_array effectively passed by value since requires local copy
}
FYI -. Isso é conhecido como "copy preguiçoso" ou "copy-on-write"
TL; DR
a) o método / função
b) o método / função modifica o argumento array => valor
c) o argumento de matriz método / função é explicitamente marcado como uma referência (com um e comercial) =>
Ou este:
-
- E comercial série param : passado por referência; as operações de escrita alterar a matriz original.
Lembre-se - PHP faz um valor-copy do momento que você escrever para o comercial não-array param. Isso é o que significa copy-on-write
. Eu adoraria mostrar-lhe a fonte C deste comportamento, mas é assustador lá. Melhor utilização xdebug_debug_zval () .
Pascal MARTIN estava certo. Kosta Kontos foi ainda mais.
Resposta ??h1>
Depende.
Versão longa
Eu acho que estou escrevendo isso para mim. I deve ter um blog ou algo assim ...
Sempre que as pessoas falam de referências (ou ponteiros, para que o assunto), eles geralmente acabam em um logomaquia (basta olhar para este fio !).
PHP ser uma linguagem venerável, eu pensei que eu deveria adicionar-se à confusão (embora este um resumo das respostas acima). Porque, apesar de duas pessoas pode estar certo, ao mesmo tempo, você é melhor fora apenas quebrando suas cabeças juntas em uma resposta.
Primeiro, você deve saber que você não é um pedante se você não responder de forma preto-e-branco . As coisas são mais complicadas do que "sim / não".
Como você vai ver, o todo por valor / por referência coisa está muito relacionado com o que exatamente você está fazendo com essa matriz em seu método escopo / função:? Lê-lo ou modificá-lo
O que PHP diz? (Aka "mudança-wise")
O manual do diz esta (grifo meu):
Por padrão, argumentos de função são passados ??por valor (de modo que, se o valor do parâmetro dentro da função é mudado , ele não fica alterado fora da função). Para permitir que uma função Modificar a sua argumentos, eles devem ser passado por referência .
Para ter um argumento para uma função sempre passado por referência, preceder um comercial (&) ao nome de argumento na definição da função
Tanto quanto eu posso dizer, quando grandes e graves, programadores honesto-a-Deus falar sobre referências, eles costumam falar sobre alterando o valor dessa referência . E isso é exatamente o que as negociações manuais sobre:. hey, if you want to CHANGE the value in a function, consider that PHP's doing "pass-by-value"
Há um outro caso em que eles não mencionam, no entanto: o que se eu não mudar nada - apenas ler
?
E se você passar um array para um método que não explicitamente marcas de referência, e não mudarmos essa matriz no âmbito da função? Por exemplo:.
<?php
function readAndDoStuffWithAnArray($array)
{
return $array[0] + $array[1] + $array[2];
}
$x = array(1, 2, 3);
echo readAndDoStuffWithAnArray($x);
Leia, meu companheiro de viagem.
O que PHP realmente fazer? (Aka "memória-wise")
As mesmas grandes e graves programadores, quando chegar ainda mais grave, eles falam sobre "otimizações de memória" no que diz respeito às referências. O mesmo acontece com PHP. Porque PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting
, isso é por .
Não seria ideal para passar matrizes enormes para várias funções, e PHP para fazer cópias deles (que é o "pass-by-value" faz, afinal de contas):
<?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);
BemAgora, se isso realmente era passagem por valor, teríamos alguns 3MB + RAM ido embora, porque há dois cópias desse array, certo?
errada. Enquanto não mudarmos a variável $arr
, que é uma referência, Memória-wise . Você simplesmente não vê-lo. É por isso que PHP menciona user-terra referências quando se fala de &$someVar
, para distinguir entre os internos e explícitas (com e comercial).
Factos
Assim, when an array is passed as an argument to a method or function is it passed by reference?
Eu vim com três (é, três) casos :
a) o método / função lê somente o argumento de matriz
b) o método / função modifica a matriz argumento
c) o argumento de matriz método / função é explicitamente marcado como uma referência (com um e comercial)
Em primeiro lugar, vamos ver a quantidade de memória que matriz realmente come (RUN aqui ):
<?php
$start_memory = memory_get_usage();
$x = array_fill(0, 10000, 1);
echo memory_get_usage() - $start_memory; // 1331840
Que muitas bytes. Ótimo.
a) o método / função só lê o argumento array
Agora vamos fazer uma função que só lê a referida matriz como um argumento e vamos ver a quantidade de memória a lógica de leitura leva:
<?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);
Quer adivinhar? Recebo 80! ver por si mesmo . Esta é a parte que os omite manual do PHP. Se o param $arr
foi realmente passou por valor, você veria algo parecido com bytes 1331840
. Parece que se comporta $arr
como uma referência, não é? Isso porque ele é A referências - um interno.
b) o método / função modifica o argumento array
Agora, vamos write para que param, em vez de ler a partir dele:
<?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);
Mais uma vez, ver por si mesmo , mas, para mim, isso é muito perto de ser 1331840. Portanto, neste caso, a matriz é realmente está sendo copiado para $arr
.
c) o argumento de matriz método / função é explicitamente marcado como uma referência (com um e comercial)
Agora vamos ver a quantidade de memória a operação de gravação para uma referência explícita ??em> leva (run aqui ) - observe o ampersand na assinatura da função:
<?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);
A minha aposta é que você começa 200 max! Portanto, este come aproximadamente tanta memória como leitura de um não-comercial param .
Por padrão
- Primitives são passados ??por valor. Não deve Java, string é primitiva em PHP
- matrizes de primitivos são passados ??por valor
- Os objetos são passados ??por referência
-
Matrizes de objectos são passados ??por valor (matriz), mas cada objecto é passado por referência.
<?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" } }
Observação: Como uma optimização, cada valor único é passado como referência até que a sua modificada dentro da função. Se for modificado eo valor foi passado por referência, em seguida, é copiado e a cópia é modificada.
Quando uma matriz é passado para um método ou função em PHP, ele é passado por valor, a menos que você passá-lo explicitamente por referência, assim:
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);
Em sua segunda pergunta, $b
não é uma referência a $a
, mas uma cópia do $a
.
Muito parecido com o primeiro exemplo, você pode fazer referência $a
fazendo o seguinte:
$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);
Esta discussão é um pouco mais velhos, mas aqui algo que eu só vim através:
Tente este código:
$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
Note que não há amp para o $ params parâmetro e ainda altera o valor de US $ arr [ 'date']. Isto realmente não combinar com todas as outras explicações aqui e que eu pensava até agora.
Se eu clonar $ params [ 'date'] objeto, a 2ª emitido estadias data da mesma. Se eu apenas configurá-lo para uma string que não afeta a saída de qualquer um.
matrizes em PHP são passados ??para funções por valor, por padrão, a menos que você passá-los explicitamente por referência, como mostra o seguinte trecho:
$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
Aqui está a saída:
array(3) {
[0]=>
int(11)
[1]=>
int(22)
[2]=>
int(33)
}
array(3) {
[0]=>
int(66)
[1]=>
int(22)
[2]=>
int(33)
}
Para estender uma das respostas, também subarrays de matrizes multidimensionais são passados ??por valor, a menos que explicitamente passou por referência.
<?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
O resultado é:
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)
}