Reputation: 1018
When I dereference an array using @$arrayRef or @{$arrayRef} it appears to create a copy of the array. Is there a correct way to dereference an array?
This code...
sub updateArray1 {
my $aRef = shift;
my @a = @$aRef;
my $aRef2 = \@a;
$a[0] = 0;
push(@a, 3);
my $aRef3 = \@a;
print "inside1 \@a: @a\n";
print "inside1 \$aRef: $aRef\n";
print "inside1 \$aRef2: $aRef2\n";
print "inside1 \$aRef3: $aRef3\n\n";
}
my @array = (1, 2);
print "before: @array\n";
my $ar = \@array;
print "before: $ar\n\n";
updateArray1(\@array);
print "after: @array\n";
$ar = \@array;
print "after: $ar\n\n";
... has the output...
before: 1 2
before: ARRAY(0x1601440)
inside1 @a: 0 2 3
inside1 $aRef: ARRAY(0x1601440)
inside1 $aRef2: ARRAY(0x30c1f08)
inside1 $aRef3: ARRAY(0x30c1f08)
after: 1 2
after: ARRAY(0x1601440)
As you can see, @$aRef creates a new pointer address.
The only way I've found to get around this is to only use the reference:
sub updateArray2 {
my $aRef = shift;
@$aRef[0] = 0;
push(@$aRef, 3);
print "inside2 \@\$aRef: @$aRef\n";
print "inside2 \$aRef: $aRef\n\n";
}
updateArray2(\@array);
print "after2: @array\n";
$ar = \@array;
print "after2: $ar\n\n";
Which produces the output:
inside2 @$aRef: 0 2 3
inside2 $aRef: ARRAY(0x1601440)
after2: 0 2 3
after2: ARRAY(0x1601440)
Is it possible to dereference a pointer to an array without the whole array getting duplicated? Or do I need to keep it in reference form and dereference it any time I want to use it?
Upvotes: 7
Views: 5454
Reputation: 66881
The code explicitly asks for data to be copied
my @a = @$aRef;
in order to create a new array @a
.
It is not clear to me what you mean by
Is it possible to dereference a pointer to an array without the whole array getting duplicated?
If all values are needed in some operation – to make another array, or print them out, or send them to sort
or map
... – then data may be copied, or it may not if that is done by pointer. If data is copied (even only on the stack) then it's the same as if we had an array, it is effectively "dereferenced."
It's a question of what is done with data and one cannot say much in general.
If you need to access a specific element (or slice) then derefence that, don't create a new array.
However, please not by @$aRef[0] = 0;
even as it happens to be legal, but by either of
$$aRef[0] = 0;
$aRef->[0] = 0;
where I find the second version to be generally safer against silly errors, and clearer.
Upvotes: 3
Reputation: 98398
Using the experimental refaliasing feature:
use 5.022;
use warnings;
use feature 'refaliasing';
no warnings 'experimental::refaliasing';
\my @array = $array_ref;
But why not just keep it as a reference? There's nothing you can do with an array that you can't also do with an array reference.
Upvotes: 5
Reputation: 6723
My observation/understanding is that it's about the name--if you give a name to the dereferenced array, it will be copied, since perl variables are passed by copy--they're exactly like structs in C++. But even if a reference is copied (this is analogous to int *newPtr = oldPtr
in C/C++), each copy of the reference will point at the same object. Only if you access its elements via the reference, or by dereferencing it inline, the original array will be modified. I don't think there's any way around this. When you call my @foo = @{$barRef}
, you're calling a copy constructor. Scalars are the same--when you modify an argument via $_[0] = val
, the caller's variable is modified as well. But if you give a name to the variable, a la my $arg = $_[0]
, a copy is made.
That said, there are convenient ways to work with references that you should know about, if you don't already use them:
$arrayRef->[0] = "foo";
$hashRef->{key} = "value";
$codeRef->(); # execute a code reference
But indeed, since push
can't deal with a reference, you need to use an inline dereference like you did in your example.
If you really, really want to create an array which is a reference, there's an old perl technique that can do it. You shouldn't use this in modern code. It's described here: https://perldoc.perl.org/perlsub.html#Passing-Symbol-Table-Entries-(typeglobs)
Upvotes: 2
Reputation: 123340
Dereferencing does not create a copy as can be seen in the following example:
my @a = qw(a b c);
my $ra = \@a;
@{$ra}[0,1] = qw(foo bar); # dereferencing is done here but not copying
print @$ra; # foo bar c
print @a; # foo bar c
Instead, assigning the (dereferenced) array to another array creates the copy:
my @a = qw(a b c);
my $ra = \@a;
my @newa = @$ra; # copy by assigning
$newa[0] = 'foo';
print @newa; # foo b c
print @a; # a b c
Assigning one array to another says essentially that all elements from the old array should be assigned to the new array too - which is different from just having a different name for the original array. But assigning one array reference to another just makes the old array available with a different name, i.e. copy array reference vs. copy array content.
Note that this seems to be different to languages like Python or Java because in these languages variables describe array objects only, i.e. the reference to the array and not the content of the array.
Upvotes: 13