字符串
一。二进制安全
数据写入时是什么样子,读出来就是什么样子,这称为二进制安全。
c语言的字符串不是二进制安全的,以\0为结束符,只能存储文本数据,不能存储图片音频等二进制数据。
举例 string.c 文件
运行后得出如下结果, a和b是相等的,长度是3,而显然a和b是不想等的,长度也应该为5,这是因为c的字符串以\0为结束符
PHP的字符串是二进制安全的,看一下PHP的代码,$a和$b是不想等的,而且长度为5
PHP实现二进制安全的主要原因是zend_string结构体的重新封装,直接以长度len来直接全部读取出来。
二。双引号和单引号转义的区别
先看上面二进制安全的例子,同一个字符串,单引号和双引号得出的结果却不一样
先查看 transfer_string.php 文件
接着调试查看存储的值为: "<?php\n$a = 'abc\\0a';\n$b = \"abc\\0a\";\n" , 这里\\即为\,\"即为",\0也是字符,为空字符。
在读取到存储的值后,会经过词法解释生成AST树,这里只简单说一下过程。
在解释到单引号时,从第一个单引号读到下一个单引号结束,中间直接返回zend_string,即字符串。
而双引号和单引号过程一样,但中间的字符会进入到zend_scan_escape_string来进行转义。即
$a = 'abc\0a' 存储为 $a = 'abc\\0a' ,读取出来时不转义为 $a = 'abc\\0a' ,长度为 6
$b = "abc\0a" 存储为 $b = \"abc\\0a\" ,读取时会经过转义为 $b = "abc\0a" , 长度为 5
可自行百度转义表
三。双引号对变量的解析
这里也涉及到词法解析生成AST树,不过多说明,过程和转义类似,单引号直接读,双引号中间会生成 ZEND_AST_ENCAPS_LIST 的AST节点。
四。字符串赋值引用计数的变化
字符串存储是共用一块内存的,通过计数来标识有多少变量引用了这块内存的值。现在看一下哪些赋值会使引用计数发生变化(对照zval的图)
1. 简单变量赋值不会发生计数,直接复制值,如 int/true/false/double/long/null等 可查看 https://www.cnblogs.com/GH-123/p/11901744.html 的整型
2. 常量字符串赋值不会发生计数,直接复制值,且 z.value.str.gc.u.v.flags 会被标志为0来记录是常量字符串
3. 引用赋值会发生计数,可查看 https://www.cnblogs.com/GH-123/p/11901744.html 的引用
4. 临时字符串,对象,资源等复杂类型一定会用到引用计数
5. 普通数组会用到引用计数,但 IS_ARRAY_IMMUTABLE (一旦创建就不可更改的数据) 不会用到引用计数
五。字符串的写时分离
只有zval为string、array、resource时,才会有写时分离,对象、传址引用等不支持。
多个变量共用同一块内存的值时,对其中一个变量进行写操作,就会触发写时分离,即重新复制一份数据给这个变量。这里会涉及到字符串的扩充,不细写
可查看 https://www.cnblogs.com/GH-123/p/11901744.html 里的引用,按照14点来操作,可发现$c的存储值的地址和b以及c是不同的了,被分离出来了。