编译顺序和后前缀运算符 [英] compilation order and post prefix opertors

查看:62
本文介绍了编译顺序和后前缀运算符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

谢谢,

解决方案

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

<小时>

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

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

关键在于知道 $a 不会将 $a 的值的副本放在堆栈上.它放置标量本身(一个 SV*,在 C 术语中).在 Perl 的行话中,我们说堆栈元素别名$a[2].在计算理论中,您会说参数是通过引用传递的.

++$a 也是如此,但 $a++ 必须在堆栈中放置一个 $a 的副本.

这意味着我们可以将上面的 printf 调用视为等价于

use Data::Alias qw( alias );{当地的 @_;别名 $_[0] = "%d %d %d %d";别名 $_[1] = $a;# 将 $a 放在堆栈上.别名 $_[2] = ++$a;# 给 $a 加 1 并将 $a 放在堆栈中.别名 $_[3] = $a++;# 将 $a 的副本放在堆栈中,并在 $a 中添加一个.别名 $_[4] = $a;# 将 $a 放在堆栈上.&CORE::printf;}

到调用 $a++ 时,$a 包含 6 个.

到调用 printf 时,$a 包含 7.

<小时>

解决方法是复制这些值.

$ perl -le'$a = 5;我的@b = ($a, ++$a, $a++, $a);打印@b";'7 7 6 7$ perl -le'$a = 5;我的@b = (0+$a, 0+(++$a), $a++, $a);打印@b";'5 6 6 7

<小时>

  1. 来自 perlop,在列表上下文中,它只是列表参数分隔符, 并将它的两个参数插入到列表中.这些参数也是从左到右计算的."

  2. 来自 perlsyn,传入的任何参数都显示在数组中 @_.因此,如果你调用一个有两个参数的函数,它们将存储在 $_[0]$_[1]. 数组 @_ 是一个本地数组,但它的元素是实际标量参数的别名."

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,

解决方案

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."

这篇关于编译顺序和后前缀运算符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆