PHP中的(++i)前缀自增 和 (i++)后缀自增
当我们学第一门语言时,比如大学课程中的C语言程序设计,也许曾经被前缀自增(i) 和后缀自增 (i)纠结过。 曾经以为我们懂了: i :先引用后增加,先在i所在的表达式中使用i的当前,后让i加1 i :先增加后引用,让i先加1,然后在i所在的表达式中使用i的新 这个
当我们学第一门语言时,比如大学课程中的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内核是如何实现这两种自增方式的呢? 看下面一个例子,在不运行这段代码的情况下,你认为会输出什么呢?
<span>$i</span> <span>=</span> <span>0</span><span>;</span> <span>$i</span> <span>=</span> <span>$i</span><span>++;</span> <span>echo</span> <span>$i</span><span>;</span>
咱们先不论答案是什么?我们直接从Zend内核查看这种自增操作的实现。
使用VLD查看包含了$i++和++$i的PHP代码生成的中间代码:
<span>$i</span> <span>=</span> <span>0</span><span>;</span> <span>$i</span><span>++;</span> <span>++</span><span>$i</span><span>;</span>
使用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<span>-></span>result.u.var).tmp_var <span>=</span> <span>**</span>var_ptr<span>;</span> zendi_zval_copy_ctor(EX_T(opline<span>-></span>result.u.var).tmp_var)<span>;</span>
这两行代码的作用是初始化返回值到临时变量,并且将原始的$i的值存储在这,这就是我们在前面使用VLD查看生成的中间代码其结果为RES[ IS_TMP_VAR ~1 ]的原因。 在这个初始化完成后,程序会继续执行增加操作,在增加操作完成后,它就结束了,而之前的++$i操作则会将result设置为UNUSED类型,这就是它少的那个操作。
后缀自增($i++)在表达式中使用的是存放在临时变量中原先的变量值,而变量本身的值已经增加了。 在PHP中这种变量的分离是通过临时变量+返回值解决。
到这里,我们可以回答最开始的问题了,它会输出0。因为在表达式中$i++的返回值是一个临时变量,也就是$i原来的值,也就是0。

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

Visual Studio Code,也稱為 VS Code,是一個免費的原始碼編輯器 - 或整合開發環境 (IDE) - 可用於所有主要作業系統。 VS Code 擁有大量針對多種程式語言的擴展,可以輕鬆編寫

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。
