浅谈PHP的数据类型
变量一般由变量名、变量值、变量类型组成。PHP中变量名与变量值可以简单的对应为:zend、zend_value。
PHP中变量的内存是通过引用计数进行管理的,在PHP7中引用计数已从之前的zval结构转移到了具体的value结构中,变量之间的赋值与传递通常也针对zend_value。PHP中通过$符号定义一个变量,在定义的同时可以初始化,这样在变量使用前不需要提前声明。
变量类型
PHP中变量类型也就是数据类型,宏观角度可分为如下几种:
- 标量类型:字符串、整型、浮点型、布尔型;
- 复合类型:数组、对象;
- 特殊类型:资源、NULL;
当然也有一些基于基础数据类型产生的特殊类型,比如引用。
内部实现
PHP中通过zval这个结构体来表示一个变量,而不同类型的变量值则通过zval嵌入的一个联合体zend_value来表示,除此之外zval还有两个特殊的union。
u1: 主要解决字节顺序问题,这里不细追究了,说实话我也看不懂;
u2: 这个结构纯粹辅助;value+u1占用空间为12byte,但系统进行字节对齐,分配16byte,于是浪费的那一点空间4byte利用起来,干点别的事情,如在散列表解决哈希冲突等;
PHP中整型,浮点型的值直接存储在zend_value中,其他类型则是指针,指向具体类型的结构。另外zend_value没有布尔型,在PHP7中拆分为了true,false两种类型,通过type类型区分,不需要具体的value了;
字符串
PHP中没有使用char来表示字符串,而是为字符串单独定义了:zend_string 结构,在zend_value中通过str指向具体的结构;
数组
这可谓是PHP最牛逼的存在,底层实现为散列表;PHP中散列表映射关系:散列函数、中间映射表、元素数组;
哈希冲突
为了解决散列表中不同元素key可能计算得到相同的哈希值,造成散列表插入冲突,PHP中把冲突的Bucket串成链表,这样一来中间映射表映射出的是一个Bucket链表,查找时需要遍历这个链表,逐个比较key,从而找到元素。Bucket会记录冲突元素在arData数组中的存储位置,在设置映射表时,如果发现中间映射表中要设置的位置已经被之前插入的元素占用了(值不是初始化的-1),那么会把已经存在的值保存到新插入的Bucket中,然后将映射表的值更新为新Bucket的存储位置,即每次会把冲突的元素插到开头。冲突的元素的保存位置没有直接放在Bucket中,而是保存在存储元素zval的u2结构中。
引用
引用并不是一种独立的类型而是一种指向其他数据类型的结构,类似C语言中的指针概念。当修改引用类型变量时,其实就是在改实际的变量。在PHP中通过&操作符生成一个引用变量:
$a = &$b
执行时首先为&操作的变量分配一个zend_reference结构,这个结构就是引用类型的结构体,内嵌了一个zval,这个zval的value指向原来zval的value,然后将原来的zval类型修改为IS_REFERENCE,原zval的value指向新创建的zend_reference结构。也就是&是将变量的类型转化为了引用,新生成的引用结构指向原来的value。
需要注意的是:引用只能通过&产生,无法通过赋值传递
1 | $a = date("Y-m-d"); |
PHP中的引用只有一级,不会出现一个引用指向另外一个引用的情况,没有类似与C语言中多级指针的概念。
类型变换
PHP是弱类型语言,使用时不需要明确定义变量类型,Zend虚拟机在执行PHP代码时会根据具体的应用场景进行装换,出了这种自动转换,PHP也提供了一种强制转换方式:
- (int) :装换为整型
- (bool):装换为布尔类型
- (float):转换为浮点型
- (string):转换为字符串
- (array):转换为数组
- (object):转换为对象
- (unset):转换为NULL
当然也有些类型是不能转换的,如资源类型。
原文作者: ybphp
原文链接: https://www.ybphp.com/2020/03/01/浅谈PHP的数据类型/
版权声明: 转载请注明出处(必须保留原文作者署名原文链接)