【PHP7源码学习】2019-03-13 PHP字符串笔记

baiyansegmentfault

所有视频:https://segmentfault.com/a/11...数组

源视频地址:http://replay.xesv5.com/ll/24...安全

字符串的设计过程

  • 在C99的柔性数组标准未发布以前,咱们若是想设计一个数据结构,存储一个字符串,能够很容易地想出以下代码:
struct string{
        ...
        int len;   //存长度(至于为何存长度下文会讲到)
        char* val; //存真正的字符串值
    };
  • 那么咱们发现,这样作有以下缺点:数据结构

    • 访问字符串值的时候,须要先访问结构体,在访问指针所指向的内存空间,须要2次内存访问,效率低下。
    • 释放字符串内存空间的时候,须要先释放char *val指针所指向的内存空间,再释放结构体自己的内存空间,效率一样低下,并且这两个操做顺序不能颠倒。
    • 那么如何改进呢?很容易想到,咱们将字符串值和结构体存储在一片连续的内存空间就能够了。这样的话,访问字符串与释放字符串的内存空间,均仅需1次内存访问,在C99柔性数组标准发布以前,改进代码的方式以下:
int main() {

    struct string{
        int len;
    };

    typedef struct string str;

    char *s = "he";
    str *p = (str*)(malloc(sizeof(str) + strlen(s) + 1)); //分配足够存下一个字符串的结构体
    p->len = strlen(s);
    memcpy(p + 1, s, strlen(s)); //将字符串拷贝到紧邻结构体的内存处

}

  • 首先咱们应该给这个结构体分配4 + 2 + 1 = 7字节的内存空间,可是因为内存对齐的缘由,最终分配了8字节大小的空间。
  • 结构体自己和len字段的地址均是0x602010,len字段的长度为4B,指针加上4B的len字段长度以后,就应该是字符串he的起始地址,即0x602014,将其强转为char *,发现正好就是咱们存的字符串值"he"。注意不是p+4,而是p+1。由于p+4 = p+4*sizeof(指针p的类型)
  • 因为这样编写代码过于繁琐,因此C99干脆制定一个标准,使用柔性数组代替上述写法。其实使用的计算方法和上面一段代码是同样的,只不过换了一种简化的写法而已,这段代码最终内存中的存储状况以下:

PHP7中字符串的实现

  • 借助上文讲到的字符串数据结构设计思想,PHP中是这样设计字符串的,它的结构体叫作zend_string:
struct _zend_string {
    zend_refcounted_h gc;         /*引用计数,与垃圾回收相关,暂不展开*/
    zend_ulong        h;          /* 冗余的hash值,计算数组key的哈希值时避免重复计算*/
    size_t            len;        /* 长度 */
    char              val[1];     /* 柔性数组,真正存放字符串值 */
};
  • 第一个问题:为何要存长度len?不存长度,直接和C语言同样经过字符串的'\0'来判断字符串结束不行吗?不行。这里有一个二进制安全的问题。spa

    • 二进制安全:写入的数据和读出来的数据彻底相同,就是二进制安全的。
    • 假设你写入了一个字符串的内容为:hello\0world,按照C语言的读取字符串的方法就会断定\0是字符串结束的标志,读出来就是hello,这样读出来的数据就和写入的数据不一致,就是非二进制安全的。
    • 若是存了长度,就不会管你是否有\0,从头开始读字符串,一直读len长度为止便可。
  • 第二个问题:最后一个字段改为char val[0]能够吗?能够。写成char val[1]是出于可移植性的考虑。有些编译器不支持[0]数组,可将其改为[]或[1]都可。
相关文章
相关标签/搜索