Переменные и ссылки

VTK

Новичок
Переменные и ссылки

У меня возникла проблема при копировании объекта, максимальное её упрощение привело к следующему вопросу: Почему результатом работы
PHP:
    $a = array( 1 );
    $c = $a;
    $c[ 0 ] = 2;
    echo $a[ 0 ];

    $b = & $a[ 0 ];
    $c = $a;
    $c[ 0 ] = 2;
    echo $a[ 0 ];
будет:

array(1) {
[0]=>
int(1)
}
array(1) {
[0]=>
&int(2)
}

?
В руководстве я нашел, что "При копировании массива ссылок, они не разыменовываются",
но я не нашел ответ на вопрос: почему в строке "$b = & $a[ 0 ];" $a[ 0 ] стала ссылкой?
Кто-нибуль можете объяснить или поделится ссылками на объяснение такого поведения?

-~{}~ 23.07.06 14:47:

Извиняюсь. Не
PHP:
echo $a[ 0 ];
, а
PHP:
var_dump( $a );
 

eXtreme

Новичок
В документации сказано:
PHP:
<?php
$a =& $b;
?>
Замечание: $a и $b здесь абсолютно эквивалентны, но это не означает, что $a указывает на $b или наоборот. Это означает, что $a и $b указывают на одно место.

Т.е. обе переменные становятся ссылками.

PHP:
$a = array( 1 ); 
$c = $a; 
$c[ 0 ] = 2; 
echo $a[ 0 ]; 

$b = & $a[ 0 ]; // $a и $b указывают на одно и тоже место
$c = $a; // $a, $b и $c указывают на одно и тоже место
$c[ 0 ] = 2; // все 3 ссылки указывают на значение 2
echo $a[ 0 ];
Так что тут всё нормально.
 

vasa_c

Новичок
>$b = & $a[ 0 ]; // $a и $b указывают на одно и тоже место
>$c = $a; // $a, $b и $c указывают на одно и тоже место
>$c[ 0 ] = 2; // все 3 ссылки указывают на значение 2
>echo $a[ 0 ];

Нет.
b эквивалентна a[ 0 ]
c не эквивалентно ни a ни b
c[ 0 ] не затронет ни a ни b
хоть бы протестировал сначала.

>Т.е. обе переменные становятся ссылками.
Они не становятся ссылками. ИМЕНА переменных являются ссылками на ЗНАЧЕНИЯ переменных с самого начала.
 

eXtreme

Новичок
PHP:
$b = & $a[0]; // $b эквивалентна $a[0]. Согласен
$c = $a; // $c не эквивалентно ни $a ни $b. Не согласен. $c эквивалентно $a. $c[0] эквивалентно $b
$c[0] = 2; // $c[0] не затронет ни $a ни $b. Не согласен. Не затронет $a, а $a[0], но затронет $b
:)
 

vasa_c

Новичок
>$c = $a; // $c не эквивалентно ни $a ни $b. Не согласен. $c эквивалентно $a. $c[0] эквивалентно $b
Ну, правильнее $c[ 0 ] эквивалентно $a[ 0 ]

$c[0] = 2; // $c[0] не затронет ни $a ни $b. Не согласен. Не затронет $a, а $a[0], но затронет $b
Да. Был не прав.
 

eXtreme

Новичок
vasa_c
Кто б с этим спорил... И вообще причём тут Си? мы говорим о PHP.
Если что-то в нём называется ссылкой, то думаю так и следует это называть.

Отвечая на вопрос автора топика: почему в строке "$b = & $a[ 0 ];" $a[ 0 ] стала ссылкой?

Думаю сильно не ошибусь, если скажу, что $a[0] и была ссылкой, но функция var_dump стала отображать её таковой только после того как появилась ещё одна ссылка на это же значение в виде $b.

-~{}~ 23.07.06 22:49:

Эх vasa_c ты уже отредактировал пост :) Мой получился не в тему...

-~{}~ 23.07.06 22:55:

Всё равно дискуссия помогла лучше разобраться в теме. Всем спасибо!
 

VTK

Новичок
Я считаю первое сообщение eXtreme наиболее близко к действительности. Дело в том, что $a[ 0 ] именно становится ссылкой, потому что до $b = & $a[ 0 ], слудующий код не затрагивал $a[ 0 ]
Код:
    $c = $a; 
    $c[ 0 ] = 2;
, но после $a[ 0 ] и $c[ 0 ] становятся ссылками на одно и тоже. Отсюда, по моему, следует что ссылки и переменные в PHP не одно и тоже.

-~{}~ 24.07.06 06:31:

проверялось в PHP 4.4.0
 

mani13

Новичок
Попробуйте написать на bugs.php.net -- там вам всё лучше объяснят, если не правы, либо скажут, что это действительно bug.
Ибо
PHP:
$c = $a;
Должна КОПИРОВАТЬ.
 

eXtreme

Новичок
Копировать что?

Перед $c = $a;
Была строка:
$b = & $a[0];
Т.е. $a - массив в нулевом индексе которого ссылка на значение на которое также указывает и $b

$c = $a и копирует этот массив со ссылкой. Ведь ссылкой становится не $c, а $c[0]

В результате:
$a и $c - массивы
$a[0], $b и $c[0] - ссылки
 

eXtreme

Новичок
В контексте PHP это не баг, а фича наверно :)
Хотя где-то могу и ошибаться.
 

mani13

Новичок
eXtreme
Давай до конца придираться.

Есть кусок памяти под массив a.
Делаем ссылку $b.
Что это есть в контексте PHP? Правильно -- УКАЗАНИЕ на кусок памяти массива $a.
Что делает копирование массива? Правильно -- КОПИРУЕТ весь кусок памяти на новое место И присваивает $c указатель на НОВЫЙ кусок памяти.
Таким образом, что мы должно иметь?
1. Ссылка указывает на СТАРЫЙ кусок памяти, выделенный под $a.
2. $c есть НОВЫЙ кусок памяти.

Предполагаю, что разрабы просто перемудрили в copy-on-write, который вроде как используется.

С другой стороны.
Так как $b и $a[0] указывают на один кусок памяти, то есть ОБА становятся ссылками(как отмечено в первом вашем посте).
То есть, при копировании копируется именно ссылка на значение...
Тогда, да, вы правы, но это, ИМХО, довольно странное поведение, которого быть НЕ ДОЛЖНО, хотя, это ведь PHP с его понимаем ссылок...
 

eXtreme

Новичок
mani13
Давай не придираться, а разбираться :)
В мануале PHP так и сказано:
Что такое ссылки
Ссылки в PHP - это средство доступа к содержимому одной переменной под разными именами. Они не похожи на указатели C и не являются псевдонимами таблицы символов. В PHP имя переменной и её содержимое - это разные вещи, поэтому одно содержимое может иметь разные имена. Ближайшая аналогия - имена файлов Unix и файлы - имена переменных являются элементами каталогов, а содержимое переменных это сами файлы. Ссылки в PHP - аналог жёстких ссылок (hardlinks) в файловых системах Unix.
PHP:
$a = array(1);
Выделяется участок памяти (условно DATA1), но то что это массив с одним элементом, что он называется $a и адрес на начало DATA1, хранится в другом служебном участке памяти.
PHP:
$b = & $a[0];
Ничего не выделяется. В служебном участке памяти появляется запись о ссылке $b, с адресом DATA1+семещение до нулевого индекса, если это смещение есть.
PHP:
$c = $a;
Копируется участок памяти DATA1 в DATA2, и... вот тут я не знаю копируеся ли нулевой индекс массива или нет, т.к. он СТАЛ ссылкой выше (наверно предусмотрено, чтобы такие данные не дублировались), но в служебном учаске памяти появляется запись о массиве с именем $c в нулевом индексе которого ссылка на DATA1+семещение до нулевого индекса, если это смещение есть. Т.е. DATA2 сейчас должна быть нулевой длины.
PHP:
$c[0] = 2;
Изменяется только DATA1. Также как если бы мы выполнили $a[0]=2 или $b=2.

Но вот, скажем, $a[1] - участок памяти DATA1, а $c[1] - участок памяти DATA2.

Вот так по моему разумению работает PHP и вы, mani13, правы в том, что это странное поведение для, скажем, Си-программера :)
 

StUV

Rotaredom
eXtreme
совершено нежизненные примеры...
ссылки надо использовать только по назначению
имхо - в 5-ом пхп все организовано оч удобно и однозначно
(говорю как отчасти С-программер ;))
 

eXtreme

Новичок
StUV - это скорей относится к теории. В теоретической информации редко наблюдаются жизненные примеры. Поэтому мы с таким трепетом вспоминаем первые курсы инста и не только поэтому :)
 

mani13

Новичок
eXtreme
Чисто в теории -- любая переменная указывает на какой-то участок памяти.

Есть:
Замечание: $a и $b здесь абсолютно эквивалентны, но это не означает, что $a указывает на $b или наоборот. Это означает, что $a и $b указывают на одно место.
Собственно, в моём понимании, это существовании такого варианта:
Код:
php > $a = 1;
php > var_dump($a, $b);
int(1)
NULL
php > $b = &$a;
php > var_dump($a, $b);
int(1)
int(1)
php > $b = 10;
php > var_dump($a, $b);
int(10)
int(10)
php > $a = 20;
php > var_dump($a, $b);
int(20)
int(20)
То есть изменяя одну из переменных, мы получаем изменение второй.
То есть мы работаем с тем, где хранится значение переменной.

Таким образом, $b есть ссылка на $a, читается как:
$b должна хранить своё значение там, где его хранит $a.

Таким образом, $a НЕ ДОЛЖНА быть ссылкой.
 

StUV

Rotaredom
Таким образом, $a НЕ ДОЛЖНА быть ссылкой
хм...
в языке без строгой типизации и слабым механизмом проверки типов "времени компиляции"* - немного странно слышать подобные рассуждения

---------
* - к формулировке не придирайтесь - кому надо тот поймет ;)...
 

mani13

Новичок
Вообще, проблема в том, что в PHP нет явного копирования)
(как Сишный memcpy, например).

На самом деле, с ссылками получается довольно смешной момент:
Код:
php > $a = 1;
php > $b = 2;
php > $c = 3;
php > $c = &$a;
php > var_dump($a, $b, $c);
int(1)
int(2)
int(1)
php > $a = &$b;
php > var_dump($a, $b, $c);
int(2)
int(2)
int(1)
То есть, как раз нету того, что $a указывает на $b. :)
Есть присвоения по ссылка, как указание на то, в каком участке памяти брать значение.
 
Сверху