http://qs321.pair.com?node_id=11124042

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

Fellow Monks,

I am still trying to wrap my head around pointers/references in Perl. I came up with the following program to help me better understand but was hoping you could please help me with some questions.

use warnings; use strict; use v5.10; my $variable = 22; my $pointer = \$variable; say "The address of \$varible, which contains the value $variable,"; say "is $pointer"; $$pointer = 25; say "Look at that! \$variable now equals $variable"; sub sum_and_diff { my $a = shift @_; my $b = shift @_; my $res = \(shift @_); # why does the "\" work here? my $sum = $a + $b; $$res = $a - $b; return $sum; } my $b = 2; my $diff; # this is line 27 my $pointer_to_diff = \$diff; say "the sum of 5 and $b is ", &sum_and_diff(5, $b, $pointer_to_diff); say "and the difference is ", $pointer_to_diff; say "the sum of 9 and $b is ", &sum_and_diff(9, $b, \$diff); say "and the difference is ", $diff; # this is line 34

(1) Does the backslash ("\") in the my $res line mean that $res contains the address of the third argument passed to the function? I think so but just wanted to confirm.

(2) Does the "double dollar sign" ("$$") two lines later mean to put the value of the difference of $a and $b in the memory location that is $res?

(3) Why do the lines using $pointer_to_diff work but the last two lines using \$diff and $diff not work? I thought that these lines were essentially equivalent and that $diff was defined in line 27. Instead, I get the following output:

The address of $varible, which contains the value 22, is SCALAR(0x801e64540) Look at that! $variable now equals 25 the sum of 5 and 2 is 7 and the difference is 3 the sum of 9 and 2 is 11 Use of uninitialized value $diff in say at line 34. and the difference is

Gratias tibi ago
Leudwinus

Edited to add: I thought using ${\$diff} in the last line would work but that too gave me the same error.

Replies are listed 'Best First'.
Re: Pointers and References
by GrandFather (Saint) on Nov 23, 2020 at 04:20 UTC

    Perl doesn't have "pointers". Sure, under the hood they are everywhere, but it's not useful to think about pointers in Perl. The reason we talk about references is that there is some extraordinarily useful under the hood baggage associated with references. Perl uses reference counted memory management. A reference to something else is one type of data that a scalar variable can contain. You can have references to pretty much anything and you can use ref to tell you what you have a reference to. That sums up to, you hardly ever need to think about memory management in Perl and most things "just work".

    So, in answer to question 1: no, it contains a reference (which might be an address of something, but you don't {and shouldn't} need to know what).

    $ in Perl as a sigil means "give me a scalar value". In answer to 2: $$ means give me the scalar referred to by the scalar xxx. Because you can assign values to a scalar when you use $$ in an assignment you are assigning to the scalar referred to by the scalar. No pointers to be seen here.

    A more useful example is:

    use warnings; use strict; use v5.10; do { my $variable = 22; my $refVar = \$variable; $$refVar = 25; say "Look at that! \$variable now equals $variable"; my $two = 2; my ($sum, $diff) = sum_and_diff(5, $two); say "the sum of 5 and $two is $sum"; say "and the difference is $diff"; }; sub sum_and_diff { my ($lhs, $rhs) = @_; return $lhs + $rhs, $lhs - $rhs; }

    Prints:

    Look at that! $variable now equals 25 the sum of 5 and 2 is 7 and the difference is 3

    Useful because it is a reminder that Perl doesn't need "output parameters" because it can return multiple values from a sub. References in Perl are most useful in building interesting structures which are a mixture of arrays, hashes and scalar values.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      Useful because it is a reminder that Perl doesn't need "output parameters" because it can return multiple values from a sub.

      Correct. OTOH "output parameters" are also needed and very useful, for example when an existing huge array must be modified from within a sub.

        I don't really think of mutable arguments like that as "output parameters". "I/O parameters" maybe. I the sense that C like languages use output parameters where the sole job of the parameter is to return information from the function I don't see a need for output parameters in Perl, although passing references in the parameter list could be used that way.

        The run time and memory cost of returning a reference to a large structure, or a list of references to large structures has essentially the same overhead on Perl as passing references back as parameters. Since returning a list of stuff is natural in Perl returning a list of references makes for easier to understand code than working with output parameters.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      So, in answer to question 1: no, it contains a reference (which might be an address of something, but you don't {and shouldn't} need to know what).

      Got it. Thanks for that clarification!

      $ in Perl as a sigil means "give me a scalar value". In answer to 2: $$ means give me the scalar referred to by the scalar xxx. Because you can assign values to a scalar when you use $$ in an assignment you are assigning to the scalar referred to by the scalar. No pointers to be seen here.

      Ok, I think I understand what you are saying. Taking your explanation one step further, I was wondering if I could use a "triple dollar sign" ($$$). Not that this would be idiomatic or anything but apparently you can:

      my $variable = 22; my $pointer = \$variable; say "The address of \$variable, which contains the value $variable,"; say "is $pointer"; $$pointer = 25; say "Look at that! \$variable now equals $variable"; my $double_pointer = \$pointer; $$$double_pointer = 50; say "Look at that! \$variable now equals $variable";
      which results in
      The address of $variable, which contains the value 22, is SCALAR(0x801e64540) Look at that! $variable now equals 25 $double_pointer = REF(0x801e644b0) Look at that! $variable now equals 50
      ...Perl doesn't need "output parameters" because it can return multiple values from a sub.

      I didn't know that! That is very cool!

      References in Perl are most useful in building interesting structures which are a mixture of arrays, hashes and scalar values.

      I guess I just need to keep re-reading the excellent documentation and trying this out. I implicitly understand the power of references but still struggling with applying them.

        Please, in the context of Perl at least, expunge "pointer" from your head. Perl references are simply not pointers. Yes, there are pointers under the hood, but thinking of them as pointers will just make your brain hurt more.

        Yes, you can nest scalar references, but it is extraordinarily unusual to do so. Again, don't think about pointers. In other languages it can be useful to use multiple levels of indirection. In Perl you can end up with convoluted data structures containing a wild mix of references to nested data of different types, but that seldom turns into nested references to a simple scalar.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

      Really helpful explanation GrandFather

      In your example code, what is achieved by wrapping part of the code in a do block?
      Is this purely to aid legibility or does it have a functional impact? The documentaion doesn't answer this query.

        Anonymous Monk is quite right. The do block is there to wrap up a "main". I don't usually bother, but in this case a previous iteration of the sub had variables with the same name as were in the main block of code so I used the do block to limit the scope of those variables.

        As a general thing avoiding global variables is good. In larger scripts I write a formal sub main to ensure the only global variables are deliberate and explicit. So the do block is a light weight version of that practice that remained somewhat beyond its use by date, but a useful discussion point as it turns out.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

        In this case, only the latter. He is doing it as a personal practice for designating the "main routine" of his program.

        do does have definite functional uses. None of them are being leveraged here.

Re: Pointers and References
by kcott (Archbishop) on Nov 23, 2020 at 04:40 UTC

    G'day Leudwinus,

    "I am still trying to wrap my head around ..."

    You're using terms — pointer, address, memory location — which I suspect you've picked up from one or more other languages. You are then attempting to apply those terms to Perl, assuming they have the same meaning. I think this may be the source of your problems.

    I'd suggest the first thing to do would be to look at perlintro; in particular, the "Perl variable types" section. At the end of that section you'll find the gentlest of introductions to references with a list of other links to more information; I'd suggest checking out perlreftut first.

    Consider the following:

    $ perl -E ' my $x = 5; say $x; my $y = \$x; say $y; say $$y; $$y += 3; say $x; ' 5 SCALAR(0x60008a1c8) 5 8
    • $x has the value 5.
    • $y is assigned a reference to $x.
    • $y has the value SCALAR(0x60008a1c8).
    • Dereferencing $y (with $$y) gives you back $x.
    • Incrementing $$y by 3 is the same as incrementing $x by 3.
    • $x now has the value 8.

    You can reference and dereference to great depths if you want; as in this exaggerated example:

    $ perl -E 'my $x = 5; say $x; my $y = \\\\$x; $$$$$y += 3; say $x' 5 8

    The construct \(...), where ... is some list, evaluates to a list of references to each element of the list:

    $ perl -E 'say for \(qw{1 2 3})' SCALAR(0x60008a730) SCALAR(0x60008a7d8) SCALAR(0x60008a748)

    You can take references to other data types:

    $ perl -E 'my @x = qw{1 2 3}; say for @x; my $y = \@x; say $y' 1 2 3 ARRAY(0x60008a8e8)

    and dereference them:

    $ perl -E 'my @x = qw{1 2 3}; say for @x; my $y = \@x; say $y; say for + @$y' 1 2 3 ARRAY(0x60008a828) 1 2 3

    You can take references to references:

    $ perl -E 'my @x = qw{1 2 3}; say for @x; my $y = \\@x; say $y' 1 2 3 REF(0x600003e80)

    and dereference them one level at a time:

    $ perl -E 'my @x = qw{1 2 3}; say for @x; my $y = \\@x; say $y; say $$ +y; say for @$$y' 1 2 3 REF(0x600003e80) ARRAY(0x60008a868) 1 2 3

    I suggest you play around with examples like these to get a better understanding of how all of this works.

    Also note that I didn't use, or indeed need, terms such as pointer, address or memory location.

    You used strict and warnings in your OP which is very good. I suggest you do the same with oneliners. Here's a common alias I use; you might want to set up something similar for yourself (although, perhaps, one a little less involved).

    $ alias perle alias perle='perl -Mstrict -Mwarnings -Mautodie=:all -MCarp::Always -E +'

    That will pick up things like this:

    $ perl -E '$x =5' $ perle '$x =5' Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) ... $ perl -E 'my $x = 5; say @$x' $ perle 'my $x = 5; say @$x' Can't use string ("5") as an ARRAY ref while "strict refs" in use ...

    — Ken

      Hi Ken,

      You're using terms — pointer, address, memory location — which I suspect you've picked up from one or more other languages. You are then attempting to apply those terms to Perl, assuming they have the same meaning. I think this may be the source of your problems.

      Guilty as charged! I was trying to replicate in Perl the following program from a C tutorial on functions and pointers:

      #include <stdio.h> int sum_and_diff (int a, int b, int *res) { int sum; sum = a + b; *res = a – b; return sum; } void main (void) { int b = 2; int diff; printf ("The sum of 5 and %d is %d\n", b, sum_and_diff (5, b, &diff)); printf ("The difference of 5 and %d is %d\n", b, diff); }

      Perhaps I just need to focus on one language at a time! And thank you for the detailed example and explanation!

      You can reference and dereference to great depths if you want; as in this exaggerated example

      I'm just reading this now but came to the same conclusion earlier today when I was reading some of the other responses to this thread.

      I suggest you play around with examples like these to get a better understanding of how all of this works.

      Agreed. What I take away from your examples is that when I try to print or output a variable that contains a reference, I can clearly see what that is in reference to. For example:

      SCALAR(0x60008a730) --> reference to a scalar ARRAY(0x60008a828) --> reference to an array REF(0x600003e80) --> reference to a reference

      This helps you understand how to dereference it:

      $$x # dereference reference to scalar (if $x is a scalar) @$x # dereference reference to array (if $x is reference to an array) @$$x # dereference reference to reference of an array

      I hope I got that last bit right!

      Also note that I didn't use, or indeed need, terms such as pointer, address or memory location.

      Duly noted! And thanks for the alias tip. I will have to park that one away for the time being because as useful as Perl one-liners are, I don't think I'm quite ready to use them that frequently.

      Gratias tibi ago
      Leudwinus

        "And thanks for the alias tip. I will have to park that one away for the time being because as useful as Perl one-liners are, I don't think I'm quite ready to use them that frequently."

        I have aliases set up such that they're always available, regardless of frequency of usage. In ~/.bashrc, I have:

        ... if [ -f "${HOME}/.bash_aliases" ]; then . "${HOME}/.bash_aliases" fi ...

        And ~/.bash_aliases has lines like this:

        ... alias vi='vim' alias view='vim -R' ... alias perle='perl -Mstrict -Mwarnings -Mautodie=:all -MCarp::Always -E +' alias perlb='perl -MO=Deparse,-p -e' ... alias apache_up='/usr/sbin/apachectl start' alias apache_down='/usr/sbin/apachectl stop' ...

        You may need to adjust to suit whatever shell you're using; however, the basic principle should be applicable to any UNIX-like system. I use this with Linux for $work; with Cygwin for personal, home use; and, up until a year or so ago, with macOS (formerly Mac OS X).

        — Ken

Re: Pointers and References
by tybalt89 (Monsignor) on Nov 23, 2020 at 02:33 UTC

    A couple lines fixed...

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11124042 use warnings; use v5.10; my $variable = 22; my $pointer = \$variable; say "The address of \$varible, which contains the value $variable,"; say "is $pointer"; $$pointer = 25; say "Look at that! \$variable now equals $variable"; sub sum_and_diff { my $a = shift @_; my $b = shift @_; # my $res = \(shift @_); # why does the "\" work here? # it didn't my $res = shift @_; my $sum = $a + $b; $$res = $a - $b; return $sum; } my $b = 2; my $diff; # this is line 27 my $pointer_to_diff = \$diff; say "the sum of 5 and $b is ", &sum_and_diff(5, $b, $pointer_to_diff); #say "and the difference is ", $pointer_to_diff; say "and the difference is ", $$pointer_to_diff; say "the sum of 9 and $b is ", &sum_and_diff(9, $b, \$diff); say "and the difference is ", $diff; # this is line 34

    Outputs:

    The address of $varible, which contains the value 22, is SCALAR(0x55d283691438) Look at that! $variable now equals 25 the sum of 5 and 2 is 7 and the difference is 3 the sum of 9 and 2 is 11 and the difference is 7
      Thank you for that!
Re: Pointers and References
by jcb (Parson) on Nov 23, 2020 at 03:40 UTC

    First, the most important difference between C pointers and Perl references is that there is no pointer arithmetic in Perl.

    Second, in answer to your questions:

    1. The @_ array is special in Perl. On entry to a sub, it contains aliases to the arguments that were passed; $_[2] = 42; will set whatever variable was used as the third argument to 42. On line 28 of your code, $pointer_to_diff will itself be set to the difference; note that printing it emits an integer instead of a reference debugging value. On line 31, you pass a newly-constructed reference to $diff instead; that reference is itself an SV, so its value gets overwritten just before it is discarded. The $diff variable itself is never actually set. Devel::Peek is likely to be helpful here.
    2. As an lvalue, it means to assign to the variable to which the reference points. Since $res is itself a reference to the alias of the parameter that was passed in, this will replace the value that was passed in. On line 28, that is the $pointer_to_diff variable. On line 31, that is an anonymous temporary constructed to hold a reference to $diff.
    3. The earlier two answers answer this as well.

    Lastly, read the documentation, particularly, perlreftut, perlref, and perldsc.

Re: Pointers and References
by erix (Prior) on Nov 23, 2020 at 14:52 UTC
Re: Pointers and References
by Leudwinus (Scribe) on Nov 25, 2020 at 17:58 UTC

    I just wanted to say thank you again to all who responded to my questions with thoughtful replies, examples and -- for a lack of a better word -- references to the Perl documentation. And apologies for taking a few days to respond back. My head hurts even more, but in a good way!

    For those monks whose monasteries are located in the United States, I hope you and your Orders have a safe and Happy Thanksgiving!

Re: Pointers and References
by Leudwinus (Scribe) on Nov 25, 2020 at 17:50 UTC

    jcb, erix, Anonymous Monk,

    Thank you for the additional explanations on the differences between references and pointers (which don't apply here) as well as on the concept of aliases (not to be confused with the Bash aliases kcott was recommending!)

Re: Pointers and References
by Anonymous Monk on Nov 23, 2020 at 03:44 UTC
    Quick conceptual tip here: "forget 'pointers!'" In high-level interpreted languages such as Perl, there are only references. The interpreter manages all of these things for you, including necessary reference-counting and memory management. "Pointers" are a much lower-level concept – part of the essential underlying implementation – which is never directly exposed to the language user.