PHP是如何存储变量的?zval结构体你了解吗?
时间:2022-05-27 [网络编程]作者:fabuyuan 浏览:5 次
PHP 源码中的 zval
在 PHP 中定义一个变量是不需要声明类型的,一开始给变量 $a 赋予一个整型值,后面又可以轻而易举地将其改变为其他类型。那在 PHP 的源码中是如何来存储这个变量 $a 的呢?带着这个疑问我们一起去看一看 PHP 的源码。
PHP 的源码是由 C 编写的,在 PHP 的源码中使用了一个 zval 的结构体来存储在 PHP 代码中创建的变量。我们把 zval 结构体的定义拿出来简单分析一下。
这是 PHP 在 Github 上的网仓库:github.com/php/php-src,本文使用的分支是 PHP-7.4.29。
zval 结构体
在 PHP 的源码中找到这个文件:php-src/Zend/zend_types.h,可以看到其中 zval 结构体的定义如下,左侧是源码。源码中使用了 PHP 自己定义的类型 zend_uchar 、uint16_t 、uint32_t 等,这些类型会针对不同平台和编译器会转为该平台下的 char short int 等。为了便于理解,我将其翻译为普通类型并展示在了源码的右侧。同时还把其中的宏函数 ZEND_ENDIAN_LOHI_3() 也展开了。
typedef struct _zval_struct zval; ... 《源代码》 《翻译后》 ------------------------------------------------------------------------------------------- struct _zval_struct { | struct _zval_struct { zend_value value; | zend_value value; union { | union { struct { | struct { ZEND_ENDIAN_LOHI_3( | unsigned char type; zend_uchar type, | unsigned char type_flags; zend_uchar type_flags, | union { union { | unsigned short extra; uint16_t extra; | } u; } u | } v; ) | unsigned int type_info; } v; | } u1; uint32_t type_info; | union { } u1; | unsigned int next; union { | unsigned int cache_slot; uint32_t next; | unsigned int opline_num; uint32_t cache_slot; | unsigned int lineno; uint32_t opline_num; | unsigned int num_args; uint32_t lineno; | unsigned int fe_pos; uint32_t num_args; | unsigned int fe_iter_idx; uint32_t fe_pos; | unsigned int access_flags; uint32_t fe_iter_idx; | unsigned int property_guard; uint32_t access_flags; | unsigned int constant_flags; uint32_t property_guard; | unsigned int extra; uint32_t constant_flags; | } u2; uint32_t extra; | }; } u2; | }; |
在 zval 结构体中,变量的值就存储在 zend_value 类型的 value 属性中。并通过 u1.v.type 来记录这个值是什么类型的,比如 IS_LONG 对应整型,IS_STRING 对应字符串类型。
zend_value 联合体
zend_value 类型也是在 php-src/Zend/zend_types.h 中定义的,是一个联合体,下面是 zend_value 联合体的定义,左侧是源码。同样在右侧我也做了简单的翻译,把 zend_long uint32_t 翻译为普通类型便于查看。
《源代码》 《翻译后》 ------------------------------------------------------------------------------------ typedef union _zend_value { | typedef union _zend_value { zend_long lval; /* long value */ | long lval; double dval; /* double value */ | double dval; zend_refcounted *counted; | zend_refcounted *counted; zend_string *str; | zend_string *str; zend_array *arr; | zend_array *arr; zend_object *obj; | zend_object *obj; zend_resource *res; | zend_resource *res; zend_reference *ref; | zend_reference *ref; zend_ast_ref *ast; | zend_ast_ref *ast; zval *zv; | zval *zv; void *ptr; | void *ptr; zend_class_entry *ce; | zend_class_entry *ce; zend_function *func; | zend_function *func; struct { | struct { uint32_t w1; | unsigned int w1; uint32_t w2; | unsigned int w2; } ww; | } ww; } zend_value; | } zend_value;
联合体的一个特点是其占用的内存是其属性中最大类型对应的长度。其中的 zend_long 就是 long 类型,可以看到 long 类型的 lval 和 double 类型的 dval 占用的长度都是 8 个字节。里面其他指针类型,也均为 8 个字节。最后面的结构体属性 ww 是由两个 int 型构成,长度相加也是 8 个字节。因此此联合体的长度为 8 个字节。
在我们写的 PHP 代码中,整型和浮点型数据的值会直接存放到 lval 和 dval 中。如果是字符串、数组以及其他类型时会开辟一段空间存储数据,并将其地址存放在 zend_value 中,也就是 zval.value 属性,如:zval.value.zend_long = 9527、zval.value.zend_string = 字符串地址 、zval.value.zend_array = 数组地址。然后在 zval.u1.v.type 上标记这个 zval.value 是整型、或浮点型、或字符串、或其他类型。
zval.u1.v.type 类型定义也是在 php-src/Zend/zend_types.h 文件中,全部的定义如下:
/* regular data types */ #define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 #define IS_TRUE 3 #define IS_LONG 4 #define IS_DOUBLE 5 #define IS_STRING 6 #define IS_ARRAY 7 #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10 /* constant expressions */ #define IS_CONSTANT_AST 11 /* internal types */ #define IS_INDIRECT 13 #define IS_PTR 14 #define IS_ALIAS_PTR 15 #define _IS_ERROR 15 /* fake types used only for type hinting (Z_TYPE(zv) can not use them) */ #define _IS_BOOL 16 #define IS_CALLABLE 17 #define IS_ITERABLE 18 #define IS_VOID 19 #define _IS_NUMBER 20
zval 结构体内存占用
接下来我们分析一下 zval 所需占用的内存。
value:zend_value 类型 8 个字节。
u1:
u1.v.type:unsigned char 1 个字节,u1.v.type_flags:unsigned char 1 个字节,u1.v.u:联合体中只有一个 unsigned short 的 extra 属性 2 个字节,因此 u1.v 的结构体总共是 4 个字节。
u1.type_info:unsigned int 4 个字节。
因此 u1 这个联合体的长度取最长的属性的长度:4 个字节。
u2:也是一个联合体,里面都是 int 型的属性,因此长度是 4 个字节。
所以 zval 总共占用的内存是 8 + 4 + 4 = 16 个字节。
也就是说当我们在写 PHP 代码时,如果创建了一个整型的变量,那么实际上它在运行中会占用 16 个字节的内存,内存开销至少是 C 语言的两倍。当然这两倍的开销也带来了 PHP 处理变量的灵活性。
推荐学习:《PHP视频教程》
以上就是PHP是如何存储变量的?zval结构体你了解吗?的详细内容,更多请关注站长家园其它相关文章!
本文标签: zval变量php
转载请注明来源:PHP是如何存储变量的?zval结构体你了解吗?
本文永久链接地址:https://www.adminjie.com/post/12853.html
免责声明:
本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
附:
二○○二年一月一日《计算机软件保护条例》第十七条规定:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬!鉴于此,也希望大家按此说明研究软件!
版权声明:
一、本站致力于为软件爱好者提供国内外软件开发技术和软件共享,着力为用户提供优资资源。
二、本站提供的部分源码下载文件为网络共享资源,请于下载后的24小时内删除。如需体验更多乐趣,还请支持正版。
三、我站提供用户下载的所有内容均转自互联网。如有内容侵犯您的版权或其他利益的,若有侵犯你的权益请:提交版权证明文件到邮箱 2225329873#qq.com(#换为@) 站长会进行审查之后,情况属实的会在三个工作日内为您删除。
更多精彩内容
- VUE中V-IF条件判断改变元素的样式操作
- Discuz如何解决安装时报错run_sql_error
- 低版本VS项目在VS2019无法正常编译的问题
- PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)
- Oracle数据库的实例/表空间/用户/表之间关系简单讲解
- RSA2是啥?PHP-RSA2签名验证怎么实现?
- app是什么应用程序的简称
- 华为dubal20是什么型号
- 小程序大小超限除了分包还能怎么做?如何避免和解决大小限制?
- ana an00华为是什么型号
- 电脑显示信号线无连接是什么意思
- html5中onclick是什么意思
- 超清视效是什么意思
- vivov1818a是什么手机型号
- html5的标题标记一共有几个等级

- 最新文章
-
-
php中restful风格是什么意思
php中restful风格指的是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义;RESTFUL适用于移动互联网厂商...
-
php中preg_replace怎么替换变量数据
在php中,“preg_replace”函数可以执行一个正则表达式的搜索和替换来替换变量数据,语法为“preg_replace(规定搜索的模式,用于替换的字符串...
-
vue抽象语法树和虚拟dom的区别是什么
vue抽象语法树和虚拟dom的区别:1、抽象语法树指的是源代码语法结构的一种抽象表示,而虚拟dom本质上就是一个普通的JS对象,用于描述视图的界面结构;2、虚拟...
-
vue3和vue2的语法有什么区别
vue3和vue2的语法区别:1、vue2使用的是webpack形式去构建项目,而vue3使用vite构建项目;2、vue2中可以使用pototype的形式去进...
-
66个面试问题,带你梳理MySQL知识点!数据库架构存储引擎日志SQL 优化索引锁事务高可用/性能运维
作为SQLBoy,基础部分不会有人不会吧?面试也不怎么问,基础掌握不错的小伙伴可以跳过这一部分。当然,可能会现场写一些SQL语句,SQ语句可以通过牛客、Lee...
-
- 热门文章
-
-
VUE中V-IF条件判断改变元素的样式操作
这篇文章主要介绍了VUE中V-IF条件判断改变元素的样式操作,具有很好的参考价值,希望对大家有所帮助。一起跟随想过来看看吧...
-
Discuz如何解决安装时报错run_sql_error
问题环境VMware虚拟机Centos7.3PHP7.0MySQL8.0NGINX1.14Discuz3.4问题还原本地环境为PHP5.6+MySQL5.6在安...
-
低版本VS项目在VS2019无法正常编译的问题
低版本VS项目在VS2019无法正常编译的问题这里指的编译并不准确,只是为了方便说明。后有(未安装),201?...
-
PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)
实现原理使用redis链表来做,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行,推荐使用。实现步骤第一步,先将商品库存入队列/**.trigge...
-
Oracle数据库的实例/表空间/用户/表之间关系简单讲解
完整的Oracle数据库通常由两部分组成:Oracle数据库和数据库实例。Oracle是一种数据库管理系统,是一种关系型的数据库管理系统。我们用这些高级权限账号...
-