PHP의 배열은 값이나 새로운 변수에 대한 참조로, 함수에 전달 될 때 복사됩니까?


259

1) 배열이 메소드 또는 함수에 인수로 전달되면 참조 또는 값으로 전달됩니까?

2) 배열을 변수에 할당 할 때 새 변수가 원래 배열에 대한 참조입니까, 아니면 새 사본입니까?
이 작업은 어떻습니까 :

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

$b대한 참조 $a입니까?



3
@MarlonJerezIsla : 함수 내에서 배열을 수정하면 배열이 복제되는 것처럼 보입니다. 여전히 다른 언어에서 나오면 이상해 보입니다.
user276648

답변:


276

질문의 두 번째 부분의 경우, 참조 설명서의 배열 페이지 상태는, (인용)를 :

배열 할당에는 항상 값 복사가 포함됩니다. 참조 연산자를 사용하여 참조로 배열을 복사하십시오.

그리고 주어진 예 :

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

마찬가지로 이번에는 배열이 "참조로"전달되었습니다.


망설이지 말고 매뉴얼의 Explained 섹션 을 읽으십시오 : 그것은 당신의 질문에 대답해야합니다 ;-)


$ a = & $ this-> a와 같은 것은 어떻습니까? $ a는 이제 & this-> a에 대한 참조입니까?
Frank

1
당신은을 사용하는 것처럼 &, 그래, 그것은해야합니다 - 볼 php.net/manual/en/...
파스칼 MARTIN

1
거룩한 암소, 나는 이것이 내가 가진 문제라고 믿을 수 없다 ... 이것이 교훈이라면, 항상
위반

2
안녕하세요 파스칼, Kosta Kontos의 답변이 더 정확한 것으로 나타났습니다. 나는 그의 발견을 확인하는 간단한 빠른 테스트 할 gist.github.com/anonymous/aaf845ae354578b74906을 당신은 자신도 발견에 댓글을 달 수 있습니까?
Cheok Yan Cheng

1
이것은 내가 가진 문제입니다 : 중첩 배열에 대해 이상하다고 생각했지만 실제로 PHP에서 배열 할당이 작동하는 방식이었습니다.
제레미 목록

120

첫 번째 질문과 관련하여 배열은 호출하는 메소드 / 함수 내에서 수정되지 않는 한 참조로 전달됩니다. 메소드 / 함수 내에서 배열을 수정하려고 시도하면 먼저 사본을 만든 다음 사본 만 수정합니다. 따라서 실제로는 그렇지 않은 경우 배열이 값으로 전달되는 것처럼 보입니다.

예를 들어,이 첫 번째 경우, 매개 변수 정의에서 & 문자를 사용하여 참조로 $ 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
}

참고로 "게으른 사본"또는 "기록 중 복사"라고합니다.


8
이것은 매우 흥미로운 정보입니다! 사실 인 것 같습니다. 하지만이 사실을 뒷받침하는 공식 문서를 찾을 수 없었습니다. 또한이 게으른 복사 개념을 지원하는 PHP 버전을 알아야합니다. 더 많은 정보가 있습니까?
Mario Awad

8
업데이트, 일부 공식 문서를 찾았지만 여전히 지연 버전을 지원하는 PHP 버전을 찾아야합니다 (매뉴얼에서 "쓰기시 복사"라고 함) : php.net/manual/en/internals2.variables.intro.php
Mario Awad

7
이것은 순수하게 PHP 가상 머신의 구현 결정이며 언어의 일부가 아닙니다. 실제로 프로그래머에게는 보이지 않습니다. 쓰기시 복사는 확실히 성능상의 이유로 권장되지만 모든 배열을 복사하는 구현은 프로그래머의 관점과 차이가 없으므로 언어 ​​의미가 값별 값을 지정한다고 말할 수 있습니다.
Superfly

14
@Superfly 메모리 부족으로 수십 개의 함수 스택을 통해 100MB 배열을 전달할 수 있는지 알고 싶을 때 확실히 차이가 있습니다! 그럼에도 불구하고 의미를 값으로 전달하는 것이 옳다는 것이 맞을 수도 있지만, 용어에 대한 이러한 문제를 피하면서 여기에 언급 된 "구현 세부 사항"은 실제 세계의 PHP 프로그래머에게 중요합니다.
Mark Amery

3
이것에 대한 또 다른 단점이 있는데, 이는 성능에 대해 생각할 때 쓰기시 복사를 인식하는 것이 훨씬 중요합니다. 참조로 배열 을 전달하면 값으로 전달하는 것과 비교하여 메모리를 절약 한다고 생각할 수 있지만 (쓰기시 복사에 대해 알지 못한 경우) 실제로 반대의 영향을 줄 수 있습니다 ! 배열이되면 이후 (자신 또는 제 3 자 코드) 값에 의해 전달, PHP는 전체 복사본을 만들거나 더 이상 참조 카운트를 추적 할 수 있습니다! 더보기 : stackoverflow.com/questions/21974581/…
Dan King

80

TL; DR

a) 메소드 / 함수 는 배열 인수 => 내재적 (내부) 참조 만 읽습니다 . b) 메소드 / 함수 배열 인수 => 값을 수정 합니다. c) 메소드 / 함수 배열 인수는 명시 적으로 참조로 표시됩니다 (앰퍼샌드) => 명시 적 (사용자 영역) 참조

또는 :
- 비 앰퍼샌드 배열 매개 변수 : 참조로 전달; 기록 동작은 어레이의 새로운 사본을 변경하며,이 사본은 첫번째 기록에서 생성된다;
- 앰퍼샌드 배열 매개 변수 : 참조로 전달됩니다. 쓰기 작업은 원래 배열을 변경합니다.

기억하십시오-PHP는 앰퍼샌드가 아닌 배열 매개 변수에 쓰는 순간 값 복사를 수행 합니다 . 그것이 copy-on-write의미 하는 바입니다. 이 행동의 C 소스를 보여 드리고 싶지만 무섭습니다. xdebug_debug_zval () 사용하는 것이 좋습니다 .

Pascal MARTIN이 옳았습니다. 코스타 콘 토스는 훨씬 더했다.

대답

때에 따라 다르지.

긴 버전

나는 이것을 직접 작성하고 있다고 생각합니다. 블로그 나 뭔가가 있어야합니다 ...

사람들이 참조 (또는 그 문제에 대한 포인터)에 대해 이야기 할 때마다 대개 로고로 마무리됩니다 (이 스레드를 보십시오!).
PHP는 훌륭한 언어이기 때문에 혼란에 가담해야한다고 생각했습니다 (이 답변에 대한 요약이지만). 두 사람이 동시에 옳을 수 있지만, 머리를 하나로 묶어 하나의 답변으로 만드는 것이 좋습니다.

우선, 당신이 흑백으로 대답하지 않으면 당신은 pedant가 아니라는 것을 알아야합니다 . "예 / 아니오"보다 상황이 더 복잡합니다.

보시다시피 전체 값 / 참조 기준은 메소드 / 함수 범위에서 해당 배열로 수행하는 작업과 관련이 있습니다. 읽거나 수정합니까?

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, 그건 .

HUGE 배열을 다양한 함수에 전달하고 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);

자, 이것이 실제로 값을 전달한 경우, 해당 어레이의 사본 이 개 있기 때문에 3MB 이상의 RAM이 사라졌습니다 .

잘못된. $arr변수를 변경하지 않는 한 메모리 기준 입니다. 당신은 그것을 보지 못합니다. 그렇기 때문에 PHP 내부 및 명시 적 (앰퍼샌드 포함)을 구별하기 위해 에 대해 이야기 할 때 사용자-랜드 참조를 언급&$someVar 합니다.

사리

그래서, when an array is passed as an argument to a method or function is it passed by reference?

나는 세 가지 (예, 세 가지) 경우를 생각해 냈습니다 :
a) 메소드 / 함수 는 배열 인수 만 읽습니다 .
b) 메소드 / 함수 배열 인수를 수정 합니다
. 앰퍼샌드)


먼저, 해당 배열이 실제로 먹는 메모리 양을 확인하십시오 ( 여기에서 실행 ).

<?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) 메소드 / 함수 배열 인수는 명시 적으로 참조로 표시됩니다 (앰퍼샌드 포함)

이제 명시 적 참조에 대한 쓰기 작업에 필요한 메모리 양을 확인하십시오 ( 여기에서 실행 )-함수 서명에서 앰퍼샌드에 유의하십시오.

<?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 최대를 얻는 것입니다! 따라서 이것은 앰퍼샌드가 아닌 매개 변수에서 읽는 것만큼이나 많은 메모리를 소비 합니다.


메모리 누수를 디버그하여 몇 시간을 절약했습니다!
Ragen Dazs

2
Kosta Kontos : 이것은 중요한 질문이므로이 질문을 받아 들인 대답으로 표시해야합니다. @nevvermind : 훌륭한 에세이이지만, TL; DR 섹션을 포함하십시오.
AVIDeveloper 2016 년

1
@nevvermind : 나는 약어 그루피가 아니며, 주요 차이점은 결론은 일반적으로 기사의 끝에 나타납니다 .TL; DR은 긴 분석을 거치지 않고 짧은 대답을 해야하는 사람들에게 첫 번째 줄로 나타납니다 . 당신의 연구는 훌륭하고 이것은 비판이 아니라 단지 내 $ 00.02입니다.
AVIDeveloper 2016 년

1
네가 옳아. 결론을 맨 위에 올렸습니다. 그러나 나는 사람들이 결론에 도달하기 전에 게으르지 말고 모든 것을 읽는 것을 멈추고 싶습니다 . 스크롤하는 것은 우리가 물건의 순서를 바꾸는 것을 귀찮게하기가 너무 쉽습니다.
nevvermind

1
나는 당신의 codepad 예는 훨씬 낮은 번호 : 제공하기 때문에 PHP가 더 효율적 년 후에 얻었다 추측
drzaus을

14

기본적으로

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

참고 : 최적화로 모든 단일 값은 함수 내에서 수정 될 때까지 참조로 전달됩니다. 수정되고 값이 참조로 전달 된 경우 복사되고 사본이 수정됩니다.


4
이 답변은 상단에 +1해야합니다. 여기에는 다른 답변에서 언급하지 않은 모호한 문제가 포함되어 있습니다. "4-개체 배열이 값 (배열)으로 전달되지만 각 개체는 참조로 전달됩니다." 그 때문에 머리를 긁고있었습니다!
augustin

@magallanes great도 나에게 가장 먼저 평가되어야합니다. 당신은 내가 가지고있는 객체 배열의 문제를 분명히합니다. 두 배열 변수 중 하나 (원본 및 사본) 중 하나에서 배열의 객체를 수정할 수있는 방법이 있습니까?
fede72bari

5

배열이 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);

1

이 스레드는 조금 낡았지만 여기에 내가 본 적이 있습니다.

이 코드를 사용해보십시오 :

$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 매개 변수에는 amp가 없지만 여전히 $ arr [ 'date']의 값을 변경합니다. 이것은 다른 모든 설명과 지금까지 생각한 것과 일치하지 않습니다.

$ params [ 'date'] 개체를 복제하면 두 번째 출력 날짜가 동일하게 유지됩니다. 방금 문자열로 설정하면 출력에도 영향을 미치지 않습니다.


3
배열이 복사되었지만 카피는 아닙니다 . 이는 숫자 및 문자열과 같은 기본 값이 $ param으로 복사되지만 객체의 경우 복제되는 객체 대신 참조가 복사됨을 의미합니다. $ arr은 $ date에 대한 참조를 보유하고 있으며 복사 된 배열 $ params도 마찬가지입니다. 따라서 $ params [ 'date']에서 값을 변경하는 함수를 호출하면 $ arr [ 'date'] 및 $ date도 변경됩니다. $ params [ 'date']를 문자열로 설정하면 $ date에 대한 $ params의 참조를 다른 것으로 바꿉니다.
ejegg

1

답 중 하나를 확장하기 위해 다차원 배열의 하위 배열도 참조로 명시 적으로 전달되지 않는 한 값으로 전달됩니다.

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

0

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)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.