http://www.perlmonks.org?node_id=1229316

aswingeo has asked for the wisdom of the Perl Monks concerning the following question:

is the example below which I got from internet correct? My doubt is - when subroutine scope is lost,@array no more remains valid.Hence returning its reference does make any sense ? Please help .

sub return_array { my @array = (1, 2, 3); foreach my $element (@array) { calculate($element); } return \@array; }

Replies are listed 'Best First'.
Re: returning reference of a variable defined inside a subroutine.
by haukex (Archbishop) on Feb 03, 2019 at 11:54 UTC

    Perl uses a method called reference counting to ensure that memory stays allocated as long as there is at least one reference pointing to it. The code you've showed is fine. On every call of the sub, a new array will be allocated, and the reference pointing to it will keep it alive, even after the sub ends.

    sub foo { my $m = shift; my @array = ($m*1, $m*2, $m*3); return \@array; } my $x = foo(2); my $y = foo(3); use Data::Dump; dd $x; # [2, 4, 6] dd $y; # [3, 6, 9] $x = undef; # *now* the memory for that array is freed

    Update: For more details, see perlref.

Re: returning reference of a variable defined inside a subroutine.
by davido (Cardinal) on Feb 04, 2019 at 06:20 UTC

    You can use Devel::Peek to inspect the reference count. Perl won't reap the underlying array until that array's reference count has dropped to zero. Here's an example:

    use Devel::Peek qw(SvREFCNT); sub return_array { my @array = (42); print "\@array declared. Refcount: ", SvREFCNT(@array), "\n"; my $ref = \@array; print "A reference has been taken to \@array. Refcount: ", SvREFCN +T(@array), "\n"; return $ref; } my $aref = return_array(); print "Function returned a reference. Refcount:", Devel::Peek::SvREFCN +T(@$aref), "\n";

    The output:

    @array declared. Refcount: 1 A reference has been taken to @array. Refcount: 2 Function returned a reference. Refcount:1

    Because a reference was returned, and that reference stays in scope for the remainder of the script, the reference count never drops below 1. Since the reference count never drops below one, the array is never reaped. So this is a safe process. In C it would be quite easy to mistakenly return a pointer to an entity that will not persist past the duration of the function call. But C isn't protected by a reference count; you have to handle memory management yourself. In C++ you do have the concept of a shared pointer, and those are reference counted with built-in memory management that more closely resembles Perl's reference counting system.


    Dave

Re: returning reference of a variable defined inside a subroutine.
by Marshall (Canon) on Feb 04, 2019 at 04:41 UTC
    The post from haukex is excellent++.

    When I looked at your "internet code", it does functionally exactly the same thing every time it runs.
    It will indeed allocate and use a new hunk of memory and return a reference to that new memory for each call, but the results in the created @array are exactly the same.

    I wrote some more demo code for you below.

    If return_array() is to produce different results upon each call, it could be that calculate() is intended to cause that result? If so, then the code as shown won't do that. Besides understanding the basics of references, the aliasing nature of a Perl for loop is also important to understand. I show a couple of ways to iterate over each element of @array, modifying @array in the process, before returning the reference to @array to the caller.

    I hope that this is instructive on what is actually a pretty complicated subject.

    #!/usr/bin/perl use strict; use warnings; use Data::Dump qw(pp); $| =1; #some advanced stuff to make the printout in the right order #forget about this for now... # a reference to an array is a scalar value.. my $ref2_array_first_run = return_array(1); my $ref2_array_second_run = return_array(4); print "Scalar value of \$ref2_array_first_run is $ref2_array_first_run +\n"; pp $ref2_array_first_run; print "Scalar value of \$ref2_array_second_run is $ref2_array_second_r +un\n"; pp $ref2_array_second_run; my @copy = @$ref2_array_first_run; # copy array to another block of # memory called @copy $ref2_array_first_run = undef; # zap memory that first call to # return_array() allocated. pp $ref2_array_first_run; # memory no long in use! pp \@copy; # A copy of the first run now # exists in other memory sub return_array { my $start_num = shift; #so that each run is different # my @array = (1, 2, 3); my @array = ($start_num..$start_num+2); # Question is whether or not calculate() should modify @array? # If so, here are 2 ways to do that... foreach my $element (@array) { # calculate($element); # does not modify @array! # these 2 calculate methods both modify @array... $element = calculate($element); # most obvious way calculate2 (\$element); # using ref to $element } return \@array; } sub calculate { my $value = shift; return ($value *5); #multiplies by 5 } sub calculate2 { my $ref2scalar = shift; $$ref2scalar *= 10; # multiplies by 10 # no "return value" needed } __END__ Prints: ## This shows that the ARRAY references created by run1 and run2 ## do indeed point to different memory Scalar value of $ref2_array_first_run is ARRAY(0x6c35e0) [50, 100, 150] # "pretty print" output for 1,2,3 Scalar value of $ref2_array_second_run is ARRAY(0xfcb430) [200, 250, 300] # "pretty_print" output for 4,5,6 undef # memory used for the first array creation is GONE. [50, 100, 150] # the @copy of the first run still exists