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

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

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

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

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



답변

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

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

그리고 주어진 예 :

<?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 섹션 을 읽으십시오 : 그것은 당신의 질문에 대답해야합니다 😉


답변

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

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

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


답변

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


답변

기본적으로

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

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


답변

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

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