编译顺序和后缀前缀运算符

时间:2013-05-15 16:31:11

标签: perl printf postfix-operator prefix-operator

我想知道为什么以下输出7 7 6 7而不是5 6 6 7

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

我很确定它与参数编译的顺序有关

谢谢,

1 个答案:

答案 0 :(得分:14)

在开始之前,请允许我指出,通常应该避免在表达式中设置和读取变量的情况。


首先,让我们看一下操作数评估顺序。这不是为许多运算符定义的,而是为列表运算符定义的。记录下按从左到右的顺序评估其操作数 [1] 。这意味着printf的参数按以下顺序进行评估:

  1. "%d %d %d %d"
  2. $a
  3. ++$a
  4. $a++
  5. $a
  6. 关键在于知道$a没有在堆栈上放置$a值的副本。它放置标量本身(用{C}表示SV*。在Perl术语中,我们说堆栈元素别名$a [2] 。在计算理论中,你会说参数是通过引用传递

    同样适用于++$a,但$a++必须在堆栈上放置$a的副本。

    这意味着我们可以将上述printf调用视为等同于

    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;
     }
    

    $a++被调用时,$a包含6。

    在调用printf时,$a包含7。


    解决方法是复制值。

    $ 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. perlop,“在列表上下文中,它只是列表参数分隔符,并将其参数插入列表。这些参数也从左到右进行评估。”

      < / LI>
    2. perlsyn开始,“传入的任何参数都显示在数组@_中。因此,如果您使用两个参数调用函数,那么这些参数将存储在$_[0]中和$_[1]。数组@_是一个本地数组,但它的元素是实际标量参数的别名。“