mysql 自增前缀_PHP中的前缀自增(++i) 和后缀自增 (i++) | 学步园

转载自:http://www.phppan.com/2011/05/pre_inc-and-post_inc-in-php/

当我们学第一门语言时,比如大学课程中的C语言程序设计,也许曾经被前缀自增(++i) 和后缀自增 (i++)纠结过。 曾经以为我们懂了:

i++ :先引用后增加,先在i所在的表达式中使用i的当前值,后让i加1

++i :先增加后引用,让i先加1,然后在i所在的表达式中使用i的新值

这个表达基本没错,只能说不够精确。在《Expert C

Programming》这本书中的附录中,有这样一段说明:

++i表示取i的地址,增加它的内容,然后把值放在寄存器中;i++表示取i的地址,把它的值装入寄存器中,然后增加内存中的i的值。

这里的寄存器存放的就是我们在表达式中使用的值。

在PHP中也有++$i和$i++,那么Zend内核是如何实现这两种自增方式的呢? 看下面一个例子,在不运行这段代码的情况下,你认为会输出什么呢?

$i = 0;

$i = $i++;

echo $i;

咱们先不论答案是什么?我们直接从Zend内核查看这种自增操作的实现。

使用VLD查看包含了$i++和++$i的PHP代码生成的中间代码:

$i = 0;

$i++;

++$i;

使用VLD命令

(php -dvld.active=1 -dvld.verbosity=3 t.php)查看详细参数:

number of ops: 8

compiled vars: !0 = $i

line # * op fetch ext return operands

--------------------------------------------------------------------------------

-

2 0 > EXT_STMT RES[ IS_UNUSED ] OP1[ IS_UNUSED ] OP2[ IS_UNUSED ]

1 ASSIGN OP1[IS_CV !0 ] OP2[ , IS_CONST (0) 0 ]

3 2 EXT_STMT RES[ IS_UNUSED ] OP1[ IS_UNUSED ] OP2[ IS_UNUSED ]

3 POST_INC RES[ IS_TMP_VAR ~1 ] OP1[ IS_CV !0 ]

4 FREE OP1[IS_TMP_VAR ~1 ]

4 5 EXT_STMT RES[ IS_UNUSED ] OP1[ IS_UNUSED ] OP2[ IS_UNUSED ]

6 PRE_INC OP1[IS_CV !0 ]

5 7 > RETURN OP1[IS_CONST (0) 1 ]

branch: # 0; line: 2- 5; sop: 0; eop: 7

path #1: 0,

从VLD扩展的输出信息可以知道,前缀自增(++$i)对应的opcode为

PRE_INC,后缀自增($i++)对应的opcode为POST_INC。

首先我们看前缀自增(++$i),++$i没有返回值或者说它的返回值为空。 根据中间代码和VLD显示的OP1的参数类型,

我们可以知道++$i的中间代码在执行是最终调用的是Zend/zend_vm_execute.h文件中的

ZEND_PRE_INC_SPEC_CV_HANDLER函数。 在ZEND_PRE_INC_SPEC_CV_HANDLER函数中有几个关键点:

CV类型变量的获取,它是调用_get_zval_ptr_ptr_cv获取CV类型变量。 这里的CV类型的变量是PHP编译期间的类似于缓存的作用,主要作用是提高某些变量的存储速度。

increment_function函数,不管是实例变量,类变量或者常规的变量,最终都是调用increment_function函数实现

变量的增加操作。

在这个函数中,程序会根据变量的类型做出不同的处理,在PHP5.3.1这个版本中,PHP支持IS_LONG、IS_DOUBLE、IS_NULL和

IS_STRING四种类型。

如果变量的类型是IS_NULL,程序会将变量的值赋值为1。如果变量类型是字符串,程序会将其转化成整形或浮点型进行计算。

使用RETURN_VALUE_UNUSED宏清除返回结果,这个宏的作用是将result变量的类型设置为EXT_TYPE_UNUSED类型。

前缀自增(++$i)操作在Zend内核中本质上是操作变量本身,而且在表达式中使用的也是这个变量本身。

了解了++$i的实现,我们来看下可能使用得更多的$i++操作的实现。

同样,从中间代码POST_INC和OP1的类型是IS_CV,我们可以在Zend/zend_vm_execute.h文件中找到其实现为

ZEND_POST_INC_SPEC_CV_HANDLER。

与前面的ZEND_PRE_INC_SPEC_CV_HANDLER相比,它们都有一个取CV类型变量的过程,也有一个

increment_function函数增加变量值的过程, 但是除此之外它多了一个操作,同时也少了一个操作。 它多的一个操作是:

EX_T(opline->result.u.var).tmp_var = **var_ptr;

zendi_zval_copy_ctor(EX_T(opline->result.u.var).tmp_var);

这两行代码的作用是初始化返回值到临时变量,并且将原始的$i的值存储在这,这就是我们在前

面使用VLD查看生成的中间代码其结果为RES[ IS_TMP_VAR ~1 ]的原因。

在这个初始化完成后,程序会继续执行增加操作,在增加操作完成后,它就结束了,而之前的++$i操作则会将result设置为UNUSED类型,这就是它

少的那个操作。

后缀自增($i++)在表达式中使用的是存放在临时变量中原先的变量值,而变量本身的值已经增加了。 在PHP中这种变量的分离是通过临时变量+返回值解决。

到这里,我们可以回答最开始的问题了,它会输出0。因为在表达式中$i++的返回值是一个临时变量,也就是$i原来的值,也就是0。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值