Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

compilation order and post prefix opertors

I was wondering why the following outputs 7 7 6 7 instead of 5 6 6 7

my $a = 5;
printf("%d %d %d %d",$a,++$a , $a++ , $a);

I'm pretty sure it has something to do with the order of parameters compilation

Thanks,

like image 374
snoofkin Avatar asked May 15 '13 16:05

snoofkin


People also ask

What are prefix operators?

Prefix Increment and Decrement Operators: ++ and --The prefix increment operator (++) adds one to its operand; this incremented value is the result of the expression. The operand must be an l-value not of type const . The result is an l-value of the same type as the operand.

What is a postfix operator?

Postfix operators are unary operators that work on a single variable which can be used to increment or decrement a value by 1(unless overloaded). There are 2 postfix operators in C++, ++ and --.

What is the difference between postfix and prefix operators explain with an example?

The main difference between prefix and postfix is that the prefix is a notation that writes the operator before operands while the postfix is a notation that writes the operator after the operands. Notation is the way of writing arithmetic expressions. There are various notations to write an arithmetic expression.

What is the precedence of the postfix operators?

Postfix increment and decrement has higher precedence than prefix increment and decrement. The operand must have integral, floating, or pointer type and must be a modifiable l-value expression (an expression without the const attribute). The result is an l-value.


1 Answers

Before I start, let me point out that one should generally avoid situations where one you both sets and reads a variable within an expression.


First, let's look at operand evaluation order. This isn't defined for many operators, but it is defined for the list operator. It's documented to evaluate its operands in left-to-right order[1]. That means that printf's arguments are evaluated in the following order:

  1. "%d %d %d %d"
  2. $a
  3. ++$a
  4. $a++
  5. $a

The key lies in knowing that $a doesn't place a copy of the value of $a on the stack. It places the scalar itself (a SV*, in C terms). In Perl jargon, we say the stack element is aliased to $a[2]. In computing theory, you'd say the arguments are passed by reference.

And the same goes for ++$a, but $a++ necessarily places a copy of $a on the stack.

This means we can view the above printf call as equivalent to

use Data::Alias qw( alias );

{
    local @_;
    alias $_[0] = "%d %d %d %d";
    alias $_[1] = $a;    # Places $a on the stack.
    alias $_[2] = ++$a;  # Adds one to $a and places $a on the stack.
    alias $_[3] = $a++;  # Places a copy of $a on the stack and adds one to $a.
    alias $_[4] = $a;    # Places $a on the stack.
    &CORE::printf;
 }

By the time $a++ is called, $a contains 6.

By the time printf is called, $a contains 7.


The workaround is to make copies of the values.

$ perl -le'$a = 5; my @b = ($a, ++$a, $a++, $a); print "@b";'
7 7 6 7

$ perl -le'$a = 5; my @b = (0+$a, 0+(++$a), $a++, $a); print "@b";'
5 6 6 7

  1. From perlop, "In list context, it's just the list argument separator, and inserts both its arguments into the list. These arguments are also evaluated from left to right."

  2. From perlsyn, "Any arguments passed in show up in the array @_. Therefore, if you called a function with two arguments, those would be stored in $_[0] and $_[1]. The array @_ is a local array, but its elements are aliases for the actual scalar parameters."

like image 101
ikegami Avatar answered Sep 18 '22 08:09

ikegami