PHP7中的数据类型
---恢复内容开始---
PHP中变量名→zval,变量值→zend_value。其变量内存是通过引用计数管理的,在PHP7中引用计数在value结构中。
变量类型:
头文件在PHP源码 /zend/zend_types.h

内部实现:
PHP通过zval这个结构体来表示一个变量,而不同类型的变量值则通过zval嵌入的一个人联合体表示,即zend_value。


zend_value是一个联合体,其代码如下:

ast、ptr、zv这些类型只给内核自己使用。
字符串:
PHP为字符串单独定义了一个结构:zend_string。在zend_value中通过str指向具体结构。



存储字符串内容的val比较特殊。
val并没有使用char*类型,字符串分配时是类似这样操作的:malloc(sizeof(zend_sting)+字符串长度),就是会多分配出一些内存来存储字符串内容,这块多出来的内存起始位置就是val。
这样做的好处可以省去一次内存分配(char*),且更有助于内存管理。
val中多出来的一个字节(结构体中为val[1]而不是val[0])用于存储存储字符串的最后一个字符"\0"。
比如$a="abc",则对应的zend_string内存结构如左图:
数组:


nTableMask:这个值在散列函数根据key的hash code银蛇元素的存储为位置时用到。nTableMask = -nTableSize 或 nTableMask = ~nTableSize+1。
nNumUsed、nNumOfElements:当删除数组元素时并不会立马从数组中删除,而是将这个元素的类型标为IS_UNDEF,只有在数组容量超限,需要扩容时才会删除。
若没有扩容,则nNumUsed将一直递增,所以其值并不是有效的元素数。nNumOfElements则是数组中有效元素的数量,所以nNumOfElements ≤ nNumUsed。
Bucket结构用力保存元素的key及value。而h是hash code:如果key是数值(及数值索引)那么它的值就是数值索引的值;如果key是字符串,那么它的值就是根据字符串key通过Time33算法计算得到的散列值。h值用来映射元素的存储位置。

数组实现:
为了实现散列表的有序性,PHP中的散列表在散列函数与元素数组之间加了一层映射表,这个映射表也是数组,大小与存储元素的数组相同。
中间映射表存储元素在实际存储的有序数组中的下标:元素按照先后顺序依次插入实际存储数组,然后将其数组下标按照散列函数散列出来的位置存储在新加的映射表中。

散列函数:根据key映射出元素的的存储位置,通常会以取模作为散列函数:key->h % nTableSize。但PHP采用另一种方式:nIndex = key->h | nTableMask。
在PHP数组的结构中并没有发现这个中间映射表,事实上,它与arData放在一起。在数组初始化时,同时分配用于存储Bucket的内存和分配相同数量的uint32_t大小的空间。然后将arData偏移到存储元素数组的位置。
中间映射表可以通过arData向前访问到。

哈希冲突:不同的key值可能计算得到相同的哈希值,在插入散列表时会发生冲突,因为映射表只能存储一个元素。
解决方法:把冲突的Bucket串成链表,即中间映射表映射出来的是一个Bucket链表,而不是一个Bucket,查找时需要遍历这个链表,逐个比较key,从而找到目标元素。
HashTable会记录与它冲突的元素在arData数组中的存储位置。

在设置映射值时,发现中间映射表中要设置的位置已经被之前插入的元素占用了(值不等于初始化的-1),那么会把已经存在的值保存到新插入的Bucket中(即c插入后u2.next=0),然后将映射表中的值更新为新Bucket的存储位置(即映射表中的值:2)。
引用:
引用是一种指向其他类型的结构,类似C语言中指针的概念。当修改引用类型的变量时,其修改将反应到实际引用的变量上。
在PHP中通过&操作符生成一个引用变量,比如$b = &$a,执行时首先为&操作的变量分配一个zend_reference结构,这个结构就是引用类型的结构体,它内嵌了一个zval,此zval的value指向原来zval的value,然后将原zval的类型修改为IS_REFERENCE,原zval的value指向新创建的zend_reference结构。


例子:
$a = date("Y-m");
$b = &$a;
$a为字符串,通过&$a将其转化为引用类型并赋值给了$b,转换后的$a的类型由IS_STRING变为IS_REFERENCE,$a的value也转变为zend_reference结构,这个结构指向原来的字符串。
$a、$b间接指向了实际的value值。

使用引用时需要注意,引用只能通过&产生,不能通过赋值传递。
如上面的例子,再把$b赋值给其他变量,那么传递给新变量的value将是实际引用的值,而不是引用本身。
$a = date("Y-m");
$b = &$a;
$c = $b; //如果想让$c也引用指向$a/$b引用的值,则:$c = &$b

---恢复内容结束---
PHP中变量名→zval,变量值→zend_value。其变量内存是通过引用计数管理的,在PHP7中引用计数在value结构中。
变量类型:
头文件在PHP源码 /zend/zend_types.h

内部实现:
PHP通过zval这个结构体来表示一个变量,而不同类型的变量值则通过zval嵌入的一个人联合体表示,即zend_value。


zend_value是一个联合体,其代码如下:

ast、ptr、zv这些类型只给内核自己使用。
字符串:
PHP为字符串单独定义了一个结构:zend_string。在zend_value中通过str指向具体结构。



存储字符串内容的val比较特殊。
val并没有使用char*类型,字符串分配时是类似这样操作的:malloc(sizeof(zend_sting)+字符串长度),就是会多分配出一些内存来存储字符串内容,这块多出来的内存起始位置就是val。
这样做的好处可以省去一次内存分配(char*),且更有助于内存管理。
val中多出来的一个字节(结构体中为val[1]而不是val[0])用于存储存储字符串的最后一个字符"\0"。
比如$a="abc",则对应的zend_string内存结构如左图:
数组:


nTableMask:这个值在散列函数根据key的hash code银蛇元素的存储为位置时用到。nTableMask = -nTableSize 或 nTableMask = ~nTableSize+1。
nNumUsed、nNumOfElements:当删除数组元素时并不会立马从数组中删除,而是将这个元素的类型标为IS_UNDEF,只有在数组容量超限,需要扩容时才会删除。
若没有扩容,则nNumUsed将一直递增,所以其值并不是有效的元素数。nNumOfElements则是数组中有效元素的数量,所以nNumOfElements ≤ nNumUsed。
Bucket结构用力保存元素的key及value。而h是hash code:如果key是数值(及数值索引)那么它的值就是数值索引的值;如果key是字符串,那么它的值就是根据字符串key通过Time33算法计算得到的散列值。h值用来映射元素的存储位置。

数组实现:
为了实现散列表的有序性,PHP中的散列表在散列函数与元素数组之间加了一层映射表,这个映射表也是数组,大小与存储元素的数组相同。
中间映射表存储元素在实际存储的有序数组中的下标:元素按照先后顺序依次插入实际存储数组,然后将其数组下标按照散列函数散列出来的位置存储在新加的映射表中。

散列函数:根据key映射出元素的的存储位置,通常会以取模作为散列函数:key->h % nTableSize。但PHP采用另一种方式:nIndex = key->h | nTableMask。
在PHP数组的结构中并没有发现这个中间映射表,事实上,它与arData放在一起。在数组初始化时,同时分配用于存储Bucket的内存和分配相同数量的uint32_t大小的空间。然后将arData偏移到存储元素数组的位置。
中间映射表可以通过arData向前访问到。

哈希冲突:不同的key值可能计算得到相同的哈希值,在插入散列表时会发生冲突,因为映射表只能存储一个元素。
解决方法:把冲突的Bucket串成链表,即中间映射表映射出来的是一个Bucket链表,而不是一个Bucket,查找时需要遍历这个链表,逐个比较key,从而找到目标元素。
HashTable会记录与它冲突的元素在arData数组中的存储位置。

在设置映射值时,发现中间映射表中要设置的位置已经被之前插入的元素占用了(值不等于初始化的-1),那么会把已经存在的值保存到新插入的Bucket中(即c插入后u2.next=0),然后将映射表中的值更新为新Bucket的存储位置(即映射表中的值:2)。
引用:
引用是一种指向其他类型的结构,类似C语言中指针的概念。当修改引用类型的变量时,其修改将反应到实际引用的变量上。
在PHP中通过&操作符生成一个引用变量,比如$b = &$a,执行时首先为&操作的变量分配一个zend_reference结构,这个结构就是引用类型的结构体,它内嵌了一个zval,此zval的value指向原来zval的value,然后将原zval的类型修改为IS_REFERENCE,原zval的value指向新创建的zend_reference结构。


例子:
$a = date("Y-m");
$b = &$a;
$a为字符串,通过&$a将其转化为引用类型并赋值给了$b,转换后的$a的类型由IS_STRING变为IS_REFERENCE,$a的value也转变为zend_reference结构,这个结构指向原来的字符串。
$a、$b间接指向了实际的value值。

使用引用时需要注意,引用只能通过&产生,不能通过赋值传递。
如上面的例子,再把$b赋值给其他变量,那么传递给新变量的value将是实际引用的值,而不是引用本身。
$a = date("Y-m");
$b = &$a;
$c = $b; //如果想让$c也引用指向$a/$b引用的值,则:$c = &$b

PHP7中的数据类型的更多相关文章
- PHP7中的数据类型(一)计数引用、写时复制,可垃圾回收
列个简单的表格说明一下:
- PHP7中我们应该学习会用的新特性
PHP7于2015年11月正式发布,本次更新可谓是PHP的重要里程碑,它将带来显著的性能改进和新特性,并对之前版本的一些特性进行改进.本文小编将和大家一起来了解探讨PHP7中的新特性. 1. 标量类型 ...
- 深入理解 PHP7 中全新的 zval 容器和引用计数机制
深入理解 PHP7 中全新的 zval 容器和引用计数机制 最近在查阅 PHP7 垃圾回收的资料的时候,网上的一些代码示例在本地环境下运行时出现了不同的结果,使我一度非常迷惑. 仔细一想不难发现问题所 ...
- JavaScript 中的数据类型
Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...
- hibernate中java类的成员变量类型如何映射到SQL中的数据类型变化
hibernate映射文件??.hbm.xml配置映射元素详解--Hibernate映射类型 在从Hibernate的java的成员类型映射到SQL中的数据类型,其内映射方式它满足,SQL可以自己调制 ...
- js中的数据类型
JS中的数据类型: ——数字 (number)NaN ——字符串(string) ——布尔 (boolean)——函数 (function) 也是对象的一种 ——对象 (object) ...
- 如何判断js中的数据类型?
js六大数据类型:number.string.object.Boolean.null.undefined string: 由单引号或双引号来说明,如"string" number: ...
- 如何判断js中的数据类型
如何判断js中的数据类型:typeof.instanceof. constructor. prototype方法比较 如何判断js中的类型呢,先举几个例子: var a = "iamstri ...
- c中的数据类型、常量、变量
一. 数据 1. 什么是数据 生活中时时刻刻都在跟数据打交道,比如体重数据.血压数据.股价数据等.在我们使用计算机的过程中,会接触到各种各样的数据,有文档数据.图片数据.视频数据,还有聊QQ时产生的文 ...
随机推荐
- Flink从入门到放弃(入门篇1)-Flink是什么
戳更多文章: 1-Flink入门 2-本地环境搭建&构建第一个Flink应用 3-DataSet API 4-DataSteam API 5-集群部署 6-分布式缓存 7-重启策略 8-Fli ...
- springboot项目容器化
创建一个简单的springboot项目,依赖中加入: 编写一个Restfull接口: 编写启动类: 启动项目,浏览器访问该接口,得到想要的结果.下面,就将这个项目进行Docker容器化(applica ...
- Flask的session使用
由于http是无状态保存的协议,session可以看作不同请求之间保存数据的一种机制.flask的session是基于cookie的会话保持. 流程 当客户端进行第一次请求的时候,客户端的HTTP r ...
- zxing 如何识别反转二维码
说起二维码扫描,估计很多人用的是 zxing 吧. 然而 zxing 虽然好用,但是却有一些坑. 这边分析一下自己实际项目遇到的一个坑. 什么坑呢? 下面举个栗子你就懂了. 这边生成二维码使用的是网络 ...
- python 自定义模块的发布和安装
[学习笔记] 自定义模块 使用的是pycharm 说白了就是.py文件都可以作为模块导入,像定义一个文件 名字为Mycode __all__ = ["add","sub& ...
- @Data注解使用后在eclipse中get/set报错解决方法
Maven项目中已经导入相关的lombok.jar包但是使用后仍提示无set/get方法 安装完成之后,请确认eclipse安装路径下是否多了一个lombok.jar包,并且其 配置文件eclipse ...
- 如何编写最佳的Dockerfile
译者按: Dockerfile 的语法非常简单,然而如何加快镜像构建速度,如何减少 Docker 镜像的大小却不是那么直观,需要积累实践经验.这篇博客可以帮助你快速掌握编写 Dockerfile 的技 ...
- 《JavaScript高级程序设计》笔记:DOM扩展(十一)
选择符API querySelector()方法 // 取得body元素 var tbody = document.querySelector('body'); // 取得ID为"myDIV ...
- tomat 欢迎页面设置在WEB-INF目录下时不显示问题
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://w ...
- python接口自动化-Cookie_绕过验证码登录
前言 有些登录的接口会有验证码,例如:短信验证码,图形验证码等,这种登录的验证码参数可以从后台获取(或者最直接的可查数据库) 获取不到也没关系,可以通过添加Cookie的方式绕过验证码 前面在“pyt ...