baiyan所有视频:https://segmentfault.com/a/11...源视频地址:http://replay.xesv5.com/ll/24...string的设计过程在C99灵活数组标准发布之前,如果我们要设计一个数据结构来存储一个string,我们很容易想出如下代码:structstring{...intlen;//存储长度(至于为什么后面会提到长度)char*val;//存储真正的字符串值};然后我们发现这样有以下缺点:访问字符串值时,需要先访问结构体,访问指针指向的内存空间时,需要两次内存访问,效率低下。释放字符串内存空间时,需要先释放char*val指针指向的内存空间,再释放结构体本身的内存空间,同样效率低下,而且这两个操作的顺序不能颠倒.那么如何改进呢?很容易想到我们可以在连续的内存空间中存储字符串值和结构。在这种情况下,只需要一次内存访问就可以访问字符串并释放字符串的内存空间。在C99灵活数组标准发布之前,改进代码的方式如下:intmain(){structstring{intlen;};typedefstruct字符串str;char*s="他";str*p=(str*)(malloc(sizeof(str)+strlen(s)+1));//分配足够的结构p来存储一个字符串->len=strlen(s);memcpy(p+1,s,strlen(s));//Copythestringtothememorynexttothestructure调试这段代码:首先我们应该为这个结构分配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_hgc;/*引用计数,与垃圾回收有关,暂不展开*/zend_ulongh;/*冗余的hash值,在计算数组key的hash值时避免重复计算*/size_tlen;/*长度*/charval[1];/*灵活的数组,实际存放的是字符串值*/};第一个问题:为什么要Storelengthlen?像C语言那样直接通过字符串的'\0'判断字符串结束不保存长度不就行了吗?不。这里有一个二进制安全问题。二进制安全:写入的数据和读取的数据完全一样,是二进制安全。假设你写了一个字符串,内容是:hello\0world,按照C语言读取字符串的方法,会判断\0是字符串结束的标志,读出的是hello,所以读出的数据将与写入的数据相同如果写入的数据不一致,则不是二进制安全的。如果保存了长度,不管你有没有\0,直接从头开始读字符串,一直读到len的长度。第二个问题:最后一个字段可以改成charval[0]吗?能。写成charval[1]是出于可移植性的考虑。有些编译器不支持[0]数组,可以改成[]或[1]。
