I have this php code
$a = array('one');
$b[0] = &$a[0];
$c = $a;
$b[0] = 'three';
$a = array('two');
var_dump($a); echo '<br/>';
var_dump($b); echo '<br/>';
var_dump($c); echo '<br/>';
Which outputs this ->
array(1) { [0]=> string(3) "two" }
array(1) { [0]=> &string(5) "three" }
array(1) { [0]=> &string(5) "three" }
$b and $a points to the same value but $c should not point to the same value, it should have its own copy of the value. Then when I change the value of $b to three I don't understand why the value of $c also changes. How can I prevent this? Also, when I change the value of $a to 'two', why doesn't $b also changes to 'two'?
Thanks in advance.
-
What are you trying to accomplish? It's hard to help if we don't know why you're making references in the first place.Ja͢ck– Ja͢ck2013年08月23日 15:40:32 +00:00Commented Aug 23, 2013 at 15:40
-
Anyone can create complex code that they don't understand. Rather than figuring out what doesn't work, why not try actually distilling what you're trying to do, and solve that problem instead. Oh, and don't use references (as a general rule)...ircmaxell– ircmaxell2013年08月23日 15:59:25 +00:00Commented Aug 23, 2013 at 15:59
-
This is what I am doing. I have to dynamically bind the variables to mysqli_stmt::bind_result so I am doing this, for($i=0; $i<$n_result; $i++) { $b_res[$i] = &$result[$i]; } call_user_func_array(array($stmt,"bind_result"),$b_res) Where $result is the number of cols returned after executing. Then, when I do this, while($stmt->fetch()) {array_push($r_val,$b_res);} all I get is the last row from the result set. This is happening because of the problem that I posted. Is there a way I can only copy the value to the new variable and then return it?maurya8888– maurya88882013年08月23日 16:22:19 +00:00Commented Aug 23, 2013 at 16:22
4 Answers 4
Your problem is:
$b[0] = &$a[0];
This references the first element of the array. As evident by the var_dump()
output &string(5)
.
So $b
does not reference the entire array, only the first element.
To reference the entire array, you need to do:
$b = &$a;
Doing so works as you expect:
// $a
array(1) {
[0]=>
string(3) "two"
}
// $b
array(1) {
[0]=>
string(3) "two"
}
// $c
array(1) {
[0]=>
string(3) "one"
}
Read more about Arrays in PHP.
This is a step-by-step explanation:
$a = array('one');
$b[0] = &$a[0];
$b
is now an array and its first element references the first element of $a
. In the symbol table, $b[0]
and $a[0]
point to the same underlying zval.
$c = $a;
$c
references $a
now, but a copy has not been made (copy on write semantics). It looks a lot like this:
$c ----/----> $a $b
(0: 'one') 0: 'one' <---- 0: 'one'
The next statement:
$b[0] = 'three';
This updates $a[0]
as well, and in turn updates $c[0]
, because $a
itself is not changed. It now looks like;
$c ----/----> $a $b
(0: 'three') 0: 'three' <---- 0: 'three'
Next statement:
$a = array('two');
Disconnect $a
from $c
and $a[0]
from $b[0]
.
$c $a $b
0: 'three' 0: 'two' 0: 'three'
Prevention
To prevent this behaviour, you need to reference the whole array instead of just one element:
$b = &$a;
By doing this, $b
and $a
now reference the same zval (the array itself), so any changes made will disconnect it from $c
.
I would consider these edge cases of the language though and I would advice not to use references if you don't need to.
-
I don't understand why $c=$a does not copy the value, but creates a reference instead. Is there any thing I can do here so that it is not a reference? Apparently that is the only thing that I can change in the code.maurya8888– maurya88882013年08月23日 15:41:40 +00:00Commented Aug 23, 2013 at 15:41
-
1@maurya8888 I've updated the answer with how it can be prevented. Of course, if you didn't use references altogether you would never have this problem either :)Ja͢ck– Ja͢ck2013年08月23日 15:44:19 +00:00Commented Aug 23, 2013 at 15:44
-
@Jack, my guess is they're coming from C given the way they referenced an array.Jason McCreary– Jason McCreary2013年08月23日 15:45:29 +00:00Commented Aug 23, 2013 at 15:45
-
Then why changing $b changes $c too? Again, Can I do something here so that changing $b does not change $c? What am I missing?maurya8888– maurya88882013年08月23日 15:46:03 +00:00Commented Aug 23, 2013 at 15:46
-
@maurya8888
$c
gets updated because the value$a[0]
gets updated (the array entry only).Ja͢ck– Ja͢ck2013年08月23日 15:47:42 +00:00Commented Aug 23, 2013 at 15:47
It's important to understand what you're doing when assigning by reference vs value. Let's go line by line.
$a = array('one');
Memory location created, we'll call it M1. $a points to M1 and within M1 we have an array with 1 entry, let's call it M1-A1.
$b[0] = &$a[0];
Now what we're doing is pointing $b[0] to M1-A1. Remember that $a[0] also points to M1-A1, so both are pointing to this particular part of memory. Remember that $b itself has it's own memory location, M2, but within M2 we point to M1-A1 (i.e. M2-A1 is pointing to M1-A1).
$c = $a;
Since we're not assigning by reference, what we get is a new memory location, let's call it M3, but within M3 there is an array with the first entry still pointing to M1-A1.
So now we have M1, M2, M3 with an array entry in M2 and M3 pointing to M1-A1.
$b[0] = 'three';
Since $b[0] actually points to M1-A1, we're actually changing the value of M1-A1 memory spot. So anything that references M1-A1's spot will also see this value change.
$a = array('two');
We're completely changing the memory location for $a at this point. Originally it was M1, now we're creating a new memory location, M4. Nothing else points to M4 and M4-A1 DOES NOT point to M1-A1.
So when we do the var dump we get the values you mentioned.
I probably made this more confusing but try drawing it on paper and it'll be pretty clear. Understand that everything is stored in memory and variables just point to memory locations. Once you understand that principle, it'll all fall in place.
-
Great! Now, can I do something so that M3 does not point to M1-A1 instead just keeps the value in M1-A1. For example - if it was C I would use * in front of $amaurya8888– maurya88882013年08月23日 16:11:06 +00:00Commented Aug 23, 2013 at 16:11
-
One little tick I've seen is to pass the array to a function. PHP will automatically remove the reference and set it as the value. So if we did $c = $a; $c = array_flip(array_flip($c)); then we'd have everything in $a as values and no longer any references. (You don't have to use array_flip, it is just an example function)SaidK– SaidK2013年08月23日 16:34:24 +00:00Commented Aug 23, 2013 at 16:34
-
Yes, something like $c = json_decode(json_encode($a)) did the trick.maurya8888– maurya88882013年08月23日 17:22:42 +00:00Commented Aug 23, 2013 at 17:22
They all refer to the same array object in memory
-
If they all refer to the same object then why var_dump($b) gives 'three', it should give 'two' as I have changed the value of $a to 'two'maurya8888– maurya88882013年08月23日 15:34:23 +00:00Commented Aug 23, 2013 at 15:34