سؤال

عند تمرير مجموعة كحجة لطريقة أو وظيفة هل تم تمريرها بالرجوع إليها؟

ماذا عن القيام بذلك:

$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

كما، هذه المرة، تم تمرير الصفيف "بالرجوع".


لا تتردد في قراءة أوضح المراجع قسم من الدليل: يجب أن تجيب على بعض أسئلتك ؛-)

نصائح أخرى

فيما يتعلق بسؤالك الأول، يتم تمرير الصفيف حسب المرجع ما لم يتم تعديله داخل الطريقة / الوظيفة التي تتصل بها. إذا حاولت تعديل الصفيف داخل الطريقة / الوظيفة، فسيتم إجراء نسخة منه أولا، ثم تم تعديل النسخة فقط. هذا يجعل يبدو أنه إذا تم تمرير الصفيف حسب القيمة عندما تكون في الحقيقة الفعلية، فليس كذلك.

على سبيل المثال، في هذه الحالة الأولى، على الرغم من أنك لا تحدد دئتك لقبول $ 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؛ دكتور

أ) الطريقة / الوظيفة يقرأ فقط وسيطة الصفيف => مرجع ضمني (داخلي)
ب) الطريقة / الوظيفة يعدل وسيطة الصفيف => القيمة
ج) يتم تمييز وسيطة صفيف الوظيفة / الوظيفة بشكل صريح كمرجع (مع Ampersand) => مرجع واضح (أراضي المستخدم)

أو هذا:
- armpersand الصفيف: مرت بالرجوع؛ تغير عمليات الكتابة نسخة جديدة من الصفيف، نسخة تم إنشاؤها على الكتابة الأولى؛
- Ampersand Array Param.: مرت بالرجوع؛ عمليات الكتابة تغير الصفيف الأصلي.

تذكر - PHP يقوم بنسخة ذات قيمة لحظة تكتب إلى المعلم غير أمبيرو Array. وهذا ما copy-on-write يعني. أحب أن أظهر لك مصدر ج هذا السلوك، لكنه مخيف هناك. أفضل استخدام XDEBUG_DEBUG_ZVAL ().

باسكال مارتن كان على حق. كان كوستا كونتوس أكثر من ذلك.

إجابه

هذا يعتمد.

نسخة طويلة

أعتقد أنني أكتب هذا لنفسي. يجب أن يكون لدي مدونة أو شيء ما ...

عندما يتحدث الناس عن المراجع (أو المؤشرات، لهذه المسألة)، عادة ما ينتهي بهم الأمر في تسجيل الدخول (انظر فقط إلى هذا مسلك!).
فب كونها لغة رائعة، اعتقدت أنني يجب أن أضيف إلى الارتباك (على الرغم من أن هذا ملخص للإجابات المذكورة أعلاه). لأنه، على الرغم من أن شخصين يمكن أن يكون صحيحا في نفس الوقت، فأنت أفضل حالا في تكسير رؤوسهم معا في إجابة واحدة.

أولا، يجب أن تعرف ذلك أنت لست ساذجا إذا كنت لا تجيب بطريقة سوداء وبيضاء. وبعد الأمور أكثر تعقيدا من "نعم / لا".

كما سترى، فإن الشيء كله حسب القيمة / حسب المرجعية يرتبط كثيرا بما تفعله بالضبط مع هذه الصفيف في نطاق طريقتك / وظيفة الدالة: قراءة ذلك أو تعديله؟

ماذا تقول فب؟ (المعروف أيضا باسم "التغيير الحكيم")

ال كتيب يقول هذا (التركيز لي):

بشكل افتراضي، حجج الوظيفة هي مرت القيمة (بحيث إذا كانت قيمة الوسيطة داخل الوظيفة تغير, ، لا يتغير خارج الوظيفة). للسماح وظيفة ل تعديل حججها، يجب أن تكون مرت بالرجوع.

للحصول على حجة إلى وظيفة مرت دائما بالرجوع إليها، قم بإعداد أمبيرات (&) إلى اسم الوسيطة في تعريف الوظيفة

بقدر ما أستطيع أن أقول، عندما يتحدث مبرمون كبيرون وخطيون وصادقون إلى الله عن المراجع، وعادة ما يتحدثون عنه تغيير قيمة تلك المرجع. وبعد وهذا بالضبط ما يتحدث يدوي عنه: 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, هذا هو لماذا.

لن يكون الأمر مثاليا لتمرير صفائف ضخمة إلى مختلف الوظائف، و 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);

حسنا، إذا كان هذا بالفعل كان في الواقع قيمة تمرير، فلن يكون لدينا حوالي 3 ميغابايت + ذاكرة الوصول العشوائي، لأن هناك اثنين نسخ من تلك الصفيف، أليس كذلك؟

خاطئ - ظلم - يظلم. طالما نحن لا نغير $arr متغير، هذا مرجع، الذاكرة الحكيمة. وبعد أنت فقط لا ترى ذلك. لهذا السبب PHP يذكر أرض المستخدم المراجع عند الحديث عن &$someVar, ، للتمييز بين تلك الداخلية والصريحة (مع أمبير) منها.

حقائق

وبالتالي، when an array is passed as an argument to a method or function is it passed by reference?

خطرت لي ثلاثة (نعم، ثلاثة) حالات:
أ) الطريقة / الوظيفة يقرأ فقط وسيطة الصفيف
ب) الطريقة / الوظيفة يعدل وسيطة الصفيف
ج) تتميز وسيطة صفيف الوظيفة / الوظيفة بشكل صريح باعتباره مرجعا (مع أمبير)


أولا، دعونا نرى مقدار الذاكرة التي تتناول في الواقع تأكل (تشغيل هنا):

<?php
$start_memory = memory_get_usage();
$x = array_fill(0, 10000, 1);
echo memory_get_usage() - $start_memory; // 1331840

أن العديد من بايت. رائعة.

أ) الطريقة / الوظيفة يقرأ فقط وسيطة الصفيف

الآن دعونا نجعل وظيفة يقرأ فقط مجموعة المذكورة كوسيطة وسنرى مقدار الذاكرة التي يأخذها منطق القراءة:

<?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 يتصرف مثل المرجع، أليس كذلك؟ هذا لأنه يكون مراجع - واحدة داخلية.

ب) الطريقة / الوظيفة يعدل وسيطة الصفيف

دعنا الآن اكتب لهذه العلامة، بدلا من القراءة منه:

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

ج) تتميز وسيطة صفيف الوظيفة / الوظيفة بشكل صريح باعتباره مرجعا (مع أمبير)

الآن دعونا نرى مقدار الذاكرة عملية الكتابة إلى مرجع واضح يأخذ (المدى هنا) - لاحظ Ampersand في توقيع الوظيفة:

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

ملاحظة لا يوجد أمبير لمعلمة Param $ ولا يزال يغير قيمة $ arr ["التاريخ"]. هذا لا يتطابق حقا مع جميع التفسيرات الأخرى هنا وما اعتقدت حتى الآن.

إذا استنسخ كائن Param US $ ["التاريخ"]، فإن تاريخ المخرجات الثاني يبقى كما هو. إذا قمت فقط بتعيينه على سلسلة لا يؤثر على الإخراج أيضا.

في صفيف 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)
}

لتمديد إحدى الإجابات، يتم أيضا تمرير صحة صفائف متعددة الأبعاد حسب القيمة إلا إذا مرت بالرجوع إليها.

<?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)
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top