Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

I need help with the error "Modification of non-creatable array value attempted"

by Hramyzn (Novice)
on Jul 14, 2014 at 16:23 UTC ( #1093577=perlquestion: print w/replies, xml ) Need Help??

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

Hello, I've decided to write a program which takes an IP address, its subnet mask and a number of hosts or subnets needed and generates a new subnet mask an prints out all of the subnets available with the new mask. I don't need help with the entire program, since I've already done it in C++ but I'm having some trouble with writing it in Perl.

The problem is that I get an error saying "Modification of non-creatable array value attempted, subscript -33" at line 8. I know that this happens if you try to modify an element of an array under a negative index, but $position is either 31, 23, 15 or 7 (at the start and shouldn't get any lower than 0 if it starts as 7). I don't know what's wrong. Here's all the code I've written until now. It's only supposed to take an IP address in x.x.x.x format and print it as a sequence of 32 bits.

sub dec_to_bin { my $number= $_[0]; my $position= $_[1]; while($number!= 0) { $adress_bin[$position]= $number % 2; $position--; $number/= 2; } } $adress_dec= "192.168.0.0"; # $adress_dec= $ARGV[0]; $old_mask= 16; # old_mask= $ARGV[1]; $num_of= 100; # num_of= $ARGV[2]; $sub_host= "host"; # sub_host= $ARGV[3]; @adress_dec= split(/\./, $adress_dec); for $i (0 .. 31) { $adress_bin[$i]= 0; } dec_to_bin($adress_dec[3], 31); dec_to_bin($adress_dec[2], 23); dec_to_bin($adress_dec[1], 15); dec_to_bin($adress_dec[0], 7); for $i (0 .. 31) { print $adress_bin[$i]; }
  • Comment on I need help with the error "Modification of non-creatable array value attempted"
  • Download Code

Replies are listed 'Best First'.
Re: I need help with the error "Modification of non-creatable array value attempted"
by Bethany (Scribe) on Jul 14, 2014 at 16:51 UTC

    Using arithmetic division as a substitute for bit manipulation can create unexpected results. Since what you're doing here is shifting bits, why not treat $number like the binary it is? Use the shift right operator >> instead of dividing by two. http://perldoc.perl.org/perlop.html#Shift-Operators

    (edited to add) Likewise, rather than using arithmetic modulo to to the equivalent of extracting the low bit, just use the bitwise and operator & to do a real bit mask operation. $number & 1 always gives the low bit of an integer $number, $number & 2 the next least significant bit, and so forth.

    If personally coding a solution for that specific chore isn't important, see the concise, ready-to-use solutions in this question and answer. http://www.perlmonks.org/?node_id=55320 I've found that nineteen times out of twenty, consulting Perl Monks Super Search and Google (which often sends me to Perl Monks) brings up a solution ready to drop in as is or use with a little adaptation.

    Hope this helps!

Re: I need help with the error "Modification of non-creatable array value attempted"
by davido (Cardinal) on Jul 14, 2014 at 16:36 UTC

    Could you provide input that produces the error message? I haven't found the winning combination with the code you posted.

    The easiest debugging step here would be to just put a "warn $position;" immediately before line 8 to verify that it is what you think it is.

    The next step will be to make your code strict/warnings compliant, which it currently isn't. That could turn up a typo somewhere in a variable name of the code you're running.


    Dave

      Thanks for the reply but I've already found the error. I've updated the code to what it currently is, EXCEPT that I've left the $number/= 2; line (which made the error appear). It seems that it tried to assign a $number % 2 to an element of an array, but it couldn't since $number wasn't guaranteed to be a whole number (and I guess that makes % fail), I've changed the $number/= 2; to $number = int($number/ 2) and now it works perfectly :)

        $number wasn't guaranteed to be a whole number (and I guess that makes % fail)

        No, Perl is OK with that:

        3:03 >perl -MData::Dump -wE "my @array; $array[5.35968 % 2] = 42; dd +\@array;" [undef, 42] 3:03 >

        But the original loop almost certainly wasn’t doing what you thought it was. With an initial value of 192:

        3:03 >perl -wE "my $iterations = 0; my $number = 192; while ($number +!= 0) { $number /= 2; ++$iterations; } say qq[$iterations iterations] +;" 1083 iterations 3:06 >

        The loop terminates when $number is zero, but (on my Perl, which is 64-bit) this test doesn’t succeed until division by two has reduced $number all the way down to half of 4.94065645841247 × 10-324 — which is a very small number!

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: I need help with the error "Modification of non-creatable array value attempted"
by Anonymous Monk on Jul 14, 2014 at 20:14 UTC

    IPv4 addresses are 32-bit; they are often handled as integer values (uint32_t).

    Perl provides pack and unpack for working with rigid-format data. For example:

    my $ip = "123.223.11.22"; my @ip = split /\./, $ip; my $p = pack "C4", @ip; # packed as 4-octet thing my $x = unpack "N", $p; # ip as unsigned int # work on integer values, e.g. masking is just ($x & $y) $p = pack "N", $x; # re-pack the value my $bitvec = unpack "B32", $p; my $quad = join q(.), unpack "C4", $p; print "as bitvector: $bitvec\n"; print "as dotted quad: $quad\n";

Re: I need help with the error "Modification of non-creatable array value attempted"
by Hramyzn (Novice) on Jul 14, 2014 at 18:19 UTC

    Thanks guys, and in the meantime, I've finished the rest of the program. Seems to work perfectly and I'm gonna post it here for everyone to see (maybe it'll be useful to someone.. who knows). Also I'll take a look at what Bethany suggested. But for now, using arithmetic division and modulo worked xD Here's the code if anyone wants it. When running it from a command line type perl than whatever you name the file followed by arguments which go: IP address to subnet, current mask, number of hosts/subnets, "hosts" or "subnets" depending on what you want and in the end, the maximum number of subnets to display. It then displays the new mask, and the subnets including their first and last IP address.

    sub dec_to_bin { my $number= $_[0]; my $position= $_[1]; while($number!= 0) { $adress_bin[$position]= $number % 2; $position--; $number= int($number/ 2); } } sub bin_to_dec { my $sum= 0; my $mult= 128; my $i; for $i($_[0] .. $_[1]) { $sum+= $mult* $adress_bin[$i]; $mult/= 2; } return $sum; } sub dec_print { print bin_to_dec(0, 7), ".", bin_to_dec(8, 15), ".", bin_to_dec(16 +, 23), ".", bin_to_dec(24, 31); } sub num_of_bits { my $i= 2; my $number= 2; while($number< $_[0]) { $i++; $number= (2** $i)-2; } return $i; } $adress_dec= $ARGV[0]; $old_mask= $ARGV[1]-1; $num_of= $ARGV[2]; $sub_host= $ARGV[3]; $max_to_show= $ARGV[4]; @adress_dec= split(/\./, $adress_dec); for $i (0 .. 31) { $adress_bin[$i]= 0; } dec_to_bin($adress_dec[3], 31); dec_to_bin($adress_dec[2], 23); dec_to_bin($adress_dec[1], 15); dec_to_bin($adress_dec[0], 7); if($sub_host eq "hosts") { $new_mask= 31- num_of_bits($num_of); } else { $new_mask= $old_mask+ num_of_bits($num_of); } print "New mask: ", $new_mask+1, "\n\n"; $to_show= (2**($new_mask- $old_mask))-2; if($to_show> $max_to_show) { $to_show= $max_to_show; } for $i(1 .. $to_show) { $temp= $i; $i2= $new_mask; while($temp!= 0) { $adress_bin[$i2]= $temp % 2; $i2--; $temp= int($temp/ 2); } for $i2($new_mask+1 .. 31) { $adress_bin[$i2]= 0; } dec_print(); print "\n\t"; $adress_bin[31]= 1; dec_print(); print "\n\t"; for $i2($new_mask+1 .. 31) { $adress_bin[$i2]= 1; } $adress_bin[31]= 0; dec_print(); print "\n"; }
Re: I need help with the error "Modification of non-creatable array value attempted"
by Hramyzn (Novice) on Jul 14, 2014 at 16:29 UTC

    Oh, I've changed the line $number/= 2; to $number= int($number /2) and now it works. I don't know why :P Got other problems now but I think I'll figure it out :D

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1093577]
Approved by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (2)
As of 2022-10-01 20:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My preferred way to holiday/vacation is:











    Results (3 votes). Check out past polls.

    Notices?