in reply to Global variable unexpectedly modified when passed by reference

The usual reason to pass a variable by reference is to allow changes in the subroutine! Pass by reference means the subroutine accesses the very same memory location as the main program. If you want the variable to remain intact, pass it by value.

use Data::Dumper; my %hash = (a => 1, b => 2); mysub(\%hash); print Dumper(\%hash); my %hash2 = (a => 1, b => 2); mysub1(%hash2); print Dumper(\%hash2); sub mysub{ my $h = shift; delete $h->{'b'}; } sub mysub1{ my %h = @_; delete $h{'b'}; }
$VAR1 = { 'a' => 1 }; $VAR1 = { 'a' => 1, 'b' => 2 };

Updated to include code example. Updated again to fix error.

1 Peter 4:10

Replies are listed 'Best First'.
Re^2: Global variable unexpectedly modified when passed by reference
by johngg (Canon) on Dec 08, 2014 at 15:07 UTC

    If you ran your code with use warnings; it would tell you that your

    my %h = shift;

    in mysub1 is not doing what you think.

    $VAR1 = { 'a' => 1 }; Odd number of elements in hash assignment at ./spw1109575_GTBT line 21 +. $VAR1 = { 'b' => 2, 'a' => 1 };

    The shift is taking the first element from the @_ array in which the %hash2 hash was passed to the subroutine and assigning that single scalar to the %h hash in the subroutine, hence the "Odd number" warning. You got the correct result from the Dump output anyway because passing by value, even the incorrect one, doesn't affect the original which is what you were illustrating.

    What you should have done is assign all of @_ to the hash

    use strict; use warnings; use Data::Dumper; my %hash = (a => 1, b => 2); mysub(\%hash); print Dumper(\%hash); my %hash2 = (a => 1, b => 2); mysub1(%hash2); print Dumper(\%hash2); sub mysub{ my $h = shift; delete $h->{'b'}; } sub mysub1{ my %h = @_; delete $h{'b'}; }

    which produces

    $VAR1 = { 'a' => 1 }; $VAR1 = { 'a' => 1, 'b' => 2 };

    I hope this is of interest.

    Cheers,

    JohnGG

      Thanks, de-referencing the passed hashref and operating on that does what I need, as this one-liner demonstrates :

      perl -w -MData::Dumper -e 'use strict; my $hash = {a => 1, b => 2}; sub mysub{my $h = shift; my %dh = %$h; delete $dh{"b"}}; mysub($hash); print Dumper($hash)'
Re^2: Global variable unexpectedly modified when passed by reference
by fshrewsb (Acolyte) on Dec 08, 2014 at 14:59 UTC

    Thanks for the clarification about the way the reference is used. The code I supplied is an example and in reality %hash would be the result of another sub call, so I can't pass by value. As I need to then call other subs with \%hash as an argument (some of which need to modify %hash), I'll need to find another way.

      Of course you can pass by value & make changes sans changes to original reference via dereference ...

      #!/perl use warnings; use strict; use 5.010; my $got = throw_ref(); my %hash = change_hash( %{ $got } ); say 'original hash now ...'; show_hash( %{ $got } ); exit; sub change_hash { my ( %h ) = @_; say 'before modification ...'; show_hash( %h ); $h{'p'} += 10; $h{'q'} = 3; say 'after modification ...'; show_hash( %h ); return %h; } sub show_hash { my ( %h ) = @_; printf "%s : %s\n" , $_ , $h{ $_ } for sort keys %h; return; } sub throw_ref { return { 'p' => -2 } ; } __END__ before modification ... p : -2 after modification ... p : 8 q : 3 original hash now ... p : -2

      ... But if the hash reference is big and/or tied, then dereference could be expensive and/or undesired.

      Sorry, this makes no sense. If the sub returns a reference to a hash, just dereference it and pass the hash. You can easily convert between scalar and reference. I suggest looking at this.

      1 Peter 4:10

      I don't understand why you can't pass by value, but no matter, you can just dereference inside the sub.

      sub mysub{ my $h = shift; my %dereferenced_hash = %$h; delete $dereferenced_hash{'b'}; }