<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; 自增</title>
	<atom:link href="https://www.phppan.com/tag/%e8%87%aa%e5%a2%9e/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 25 Apr 2026 00:56:17 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>PHP中的前缀自增(++i) 和后缀自增 (i++)</title>
		<link>https://www.phppan.com/2011/05/pre_inc-and-post_inc-in-php/</link>
		<comments>https://www.phppan.com/2011/05/pre_inc-and-post_inc-in-php/#comments</comments>
		<pubDate>Mon, 16 May 2011 01:28:21 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP应用]]></category>
		<category><![CDATA[自增]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1374</guid>
		<description><![CDATA[当我们学第一门语言时，比如大学课程中的C语言程序设计，也许曾经被前缀自增(++i) 和后缀自增 (i++)纠结 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">当我们学第一门语言时，比如大学课程中的C语言程序设计，也许曾经被前缀自增(++i) 和后缀自增 (i++)纠结过。 曾经以为我们懂了：</p>
<ul>
<li>i++ ：先引用后增加，先在i所在的表达式中使用i的当前值，后让i加1</li>
<li>++i ：先增加后引用，让i先加1，然后在i所在的表达式中使用i的新值</li>
</ul>
<p style="text-indent: 2em;">这个表达基本没错，只能说不够精确。在《Expert C Programming》这本书中的附录中，有这样一段说明： ++i表示取i的地址，增加它的内容，然后把值放在寄存器中；i++表示取i的地址，把它的值装入寄存器中，然后增加内存中的i的值。 这里的寄存器存放的就是我们在表达式中使用的值。</p>
<p style="text-indent: 2em;">在PHP中也有++$i和$i++，那么Zend内核是如何实现这两种自增方式的呢？ 看下面一个例子，在不运行这段代码的情况下，你认为会输出什么呢？</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #6d9cbe;">$i</span> <span style="color: #e0882f;">=</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
<span style="color: #6d9cbe;">$i</span> <span style="color: #e0882f;">=</span> <span style="color: #6d9cbe;">$i</span><span style="color: #e0882f;">++;</span>
<a style="color: #1299da; text-decoration: none;" href="http://www.php.net/echo"><span style="color: #e2392d;">echo</span></a> <span style="color: #6d9cbe;">$i</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">咱们先不论答案是什么？我们直接从Zend内核查看这种自增操作的实现。</p>
<p style="text-indent: 2em;">使用VLD查看包含了$i++和++$i的PHP代码生成的中间代码：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #6d9cbe;">$i</span> <span style="color: #e0882f;">=</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
<span style="color: #6d9cbe;">$i</span><span style="color: #e0882f;">++;</span>
<span style="color: #e0882f;">++</span><span style="color: #6d9cbe;">$i</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;"><a style="color: #1299da; text-decoration: underline;" href="http://www.phppan.com/2011/05/vld-extension/">使用VLD命令</a>(php -dvld.active=1 -dvld.verbosity=3 t.php)查看详细参数:</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">number of ops:  8
compiled vars:  !0 = $i
line     # *  op                           fetch          ext  return  operands
--------------------------------------------------------------------------------
-
   2     0  &gt;   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    &gt; RETURN                                                    OP1[IS_CONST (0) 1 ]

branch: #  0; line:     2-    5; sop:     0; eop:     7
path #1: 0,</pre>
<p style="text-indent: 2em;">从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函数中有几个关键点:</p>
<ul>
<li>CV类型变量的获取，它是调用_get_zval_ptr_ptr_cv获取CV类型变量。 这里的CV类型的变量是PHP编译期间的类似于缓存的作用，主要作用是提高某些变量的存储速度。</li>
<li>increment_function函数，不管是实例变量，类变量或者常规的变量，最终都是调用increment_function函数实现变量的增加操作。 在这个函数中，程序会根据变量的类型做出不同的处理，在PHP5.3.1这个版本中，PHP支持IS_LONG、IS_DOUBLE、IS_NULL和IS_STRING四种类型。 如果变量的类型是IS_NULL，程序会将变量的值赋值为1。如果变量类型是字符串，程序会将其转化成整形或浮点型进行计算。</li>
<li>使用RETURN_VALUE_UNUSED宏清除返回结果，这个宏的作用是将result变量的类型设置为EXT_TYPE_UNUSED类型。</li>
</ul>
<p style="text-indent: 2em;">前缀自增(++$i)操作在Zend内核中本质上是操作变量本身，而且在表达式中使用的也是这个变量本身。</p>
<p style="text-indent: 2em;">了解了++$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函数增加变量值的过程， 但是除此之外它多了一个操作，同时也少了一个操作。 它多的一个操作是：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">EX_T<span style="color: #ffffff;">(</span>opline<span style="color: #e0882f;">-&gt;</span>result.<span style="color: #ffffff;">u</span>.<span style="color: #ffffff;">var</span><span style="color: #ffffff;">)</span>.<span style="color: #ffffff;">tmp_var</span> <span style="color: #e0882f;">=</span> <span style="color: #e0882f;">**</span>var_ptr<span style="color: #e0882f;">;</span>
zendi_zval_copy_ctor<span style="color: #ffffff;">(</span>EX_T<span style="color: #ffffff;">(</span>opline<span style="color: #e0882f;">-&gt;</span>result.<span style="color: #ffffff;">u</span>.<span style="color: #ffffff;">var</span><span style="color: #ffffff;">)</span>.<span style="color: #ffffff;">tmp_var</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">这两行代码的作用是初始化返回值到临时变量，并且将原始的$i的值存储在这，这就是我们在前面使用VLD查看生成的中间代码其结果为RES[ IS_TMP_VAR ~1 ]的原因。 在这个初始化完成后，程序会继续执行增加操作，在增加操作完成后，它就结束了，而之前的++$i操作则会将result设置为UNUSED类型，这就是它少的那个操作。</p>
<p style="text-indent: 2em;">后缀自增($i++)在表达式中使用的是存放在临时变量中原先的变量值，而变量本身的值已经增加了。 在PHP中这种变量的分离是通过临时变量+返回值解决。</p>
<p style="text-indent: 2em;">到这里，我们可以回答最开始的问题了，它会输出0。因为在表达式中$i++的返回值是一个临时变量，也就是$i原来的值，也就是0。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/05/pre_inc-and-post_inc-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
