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

Hi. wrt the heading, is there anything to be said for passing a scalar like a string with a reference. I expect this should be considered better since it implies no coping process is used. Possibly it is of no concern for small strings (and small numbers)??

--------------------- code from web tutorial and added to by me ----------------------------------------- #!/usr/bin/perl use warnings; use strict; my @a = (1,3,2,6,8,4,9); my $m = &max(\@a); # passing an array by reference print "The max of @a is $m\n"; # prints max is 9 sub max{ my $aref = $_[0]; my $k = $aref->[0]; for(@$aref){ $k = $_ if($k < $_); } return $k; } my $string = "This is a string"; printWthStrRef(\$string); # passing string by reference sub printWthStrRef { my $aref = $_[0]; print ${$aref}; #prints the string }
---------------------------

Regards JC.....

Replies are listed 'Best First'.
Re: Passing argument by reference (for a scalar)
by etj (Priest) on Sep 07, 2024 at 15:56 UTC
    Modern Perl has an optimisation for PV scalars (including strings), where a copied scalar will only copy the pointed-to data when it is written to. With that, there's no real benefit to accessing the scalar via a reference. Before that optimisation, there would have been a benefit.

    Passing arrays and hashes by reference will avoid copying, and will (depending on scale) be the way to make things go faster.

Re: Passing argument by reference (for a scalar)
by ikegami (Patriarch) on Sep 07, 2024 at 18:38 UTC

    In Perl, arguments are always passed by reference

    use Devel::Peek qw( Dump ); sub f { Dump( $_[0] ); # ... } my $x = "abc"; Dump( $x ); f( $x ); # Only a "C pointer" copied onto the stack.
    SV = PV(0x55df7c8f0ee0) at 0x55df7c91f1e0 <-- One scalar REFCNT = 1 FLAGS = (POK,IsCOW,pPOK) PV = 0x55df7c94f190 "abc"\0 CUR = 3 LEN = 16 COW_REFCNT = 1 SV = PV(0x55df7c8f0ee0) at 0x55df7c91f1e0 <-- Same scalar REFCNT = 1 FLAGS = (POK,IsCOW,pPOK) PV = 0x55df7c94f190 "abc"\0 CUR = 3 LEN = 16 COW_REFCNT = 1

    That said, subs typically start by making a copy of the arguments.

    use Devel::Peek qw( Dump ); sub f { my $x = shift; # Scalar is copied. Dump( $x ); # ... } my $x = "abc"; Dump( $x ); f( $x ); # Only a "C pointer" copied onto the stack.
    SV = PV(0x5602d6441ee0) at 0x5602d6470148 <-- One scalar REFCNT = 1 FLAGS = (POK,IsCOW,pPOK) PV = 0x5602d6478030 "abc"\0 <-- String buffer CUR = 3 LEN = 16 COW_REFCNT = 1 SV = PV(0x5602d6441f00) at 0x5602d6470100 <-- Different scalar REFCNT = 1 FLAGS = (POK,IsCOW,pPOK) PV = 0x5602d6478030 "abc"\0 <-- String buffer CUR = 3 LEN = 16 COW_REFCNT = 2

    But notice that both scalars share the same string buffer. Since 5.20, copying a scalar containing string usually doesn't involve copying the string buffer thanks to the Copy on Write (COW) mechanism. So even when scalars are effectively passed by copy (though explicit copying), the performance cost is relatively low.

Re: Passing argument by reference (for a scalar) (Alias)
by LanX (Saint) on Sep 07, 2024 at 19:12 UTC

    "Passing by reference" ( reference in computer science lingo) is the default in Perl since $_[0] is an alias . (NB: "alias" != "reference" in Perl)

    For instance you can do

    my $string = "This is a string"; printWthStrRef($string); # passing string by alias sub printWthStrRef { my $ref = \( $_[0] ); print $$ref; # prints the string print $_[0]; # prints the string $_[0] = "new"; # overwrite original $string }

    You only need explicit references when passing multiple complex variables like arrays or hashes, to avoid flattening to lists.

    (And even here you could use prototypes, but this is too far fetched here)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

    ( disclaimer: I wrote this hours ago but the website was unresponsive for me)

Re: Passing argument by reference (for a scalar)
by harangzsolt33 (Deacon) on Sep 09, 2024 at 00:11 UTC
    I think, passing a string by reference makes sense when you work with extremely large strings. For example, if you were to store an entire image in raw uncompressed format in a string, then you better pass by reference lest you accidentally end up with copies in memory... Let's say you have a 4000x3000x24 image in memory. That's almost 40 megabytes. Now, if you read and write this string, you are better off if you pass by reference. Plus, you don't know who will run your script. Maybe someone will try to run it using an older Perl interpreter which does not have smart optimizations built into it. So, I think, it's safer when you're dealing with file contents or images or anything really big. Now, I've noticed that there is a slight toll in speed. Operations like substr($$REF, ...) are slightly slower than substr($STRING, ...) but it's negligible.

      I think, passing a string by reference makes sense when you work with extremely large string

      Please read the earlier answers which show this completely wrong. Perl always passes by reference, so there's no reason to pass a reference.

      Note that COW has a chance of failing. (The scalar needs to "owned by Perl", and its string buffer must have space to accommodate the COW count.) But even if you want to give your argument names without relying on COW, you don't have to change your code to use references. Instead, you can replace

      my $x = shift;

      with

      use experimental qw( refaliasing declared_refs ); my \$x = \shift;

      or

      use Data::Alias qw( alias ); alias my $x = shift;

      or

      our $x; local *x = \shift;
        Perl always passes by reference

        Okay, but then why is it that as soon as I do my $STRING = shift; inside my sub, Perl's memory usage doubles (using TinyPerl 5.8 under Windows XP)?

        It seems like it may be passed by reference, but as soon as you grab that using shift() or just do my $MYSTRING = $_[0]; then viola! You have another copy of the string!