资讯专栏INFORMATION COLUMN

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

blair / 449人阅读

摘要:释放字符串内存空间的时候,需要先释放指针所指向的内存空间,再释放结构体本身的内存空间,效率同样低下,而且这两个操作顺序不能颠倒。如果存了长度,就不会管你是否有,从头开始读字符串,一直读长度为止即可。有些编译器不支持数组,可将其改成或均可。

baiyan

全部视频: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)); //将字符串拷贝到紧邻结构体的内存处

}

小插曲:这个代码的第一版,我还出现了一个指针加法的错误,见:关于memcpy一个字符串到紧邻结构体内存空间处的疑问

我们利用gdb调试一下这段代码:

首先我们应该给这个结构体分配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语言一样通过字符串的""来判断字符串结束不行吗?不行。这里有一个二进制安全的问题。

二进制安全:写入的数据和读出来的数据完全相同,就是二进制安全的。

假设你写入了一个字符串的内容为:helloworld,按照C语言的读取字符串的方法就会判定是字符串结束的标志,读出来就是hello,这样读出来的数据就和写入的数据不一致,就是非二进制安全的。

如果存了长度,就不会管你是否有,从头开始读字符串,一直读len长度为止即可。

第二个问题:最后一个字段改成char val[0]可以吗?可以。写成char val[1]是出于可移植性的考虑。有些编译器不支持[0]数组,可将其改成[]或[1]均可。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31303.html

相关文章

  • 【LNMPR源码学习笔记汇总

    摘要:此文用于汇总跟随陈雷老师及团队的视频,学习源码过程中的思考整理与心得体会,此文会不断更新视频传送门每日学习记录使用录像设备记录每天的学习源码学习源码学习内存管理笔记源码学习内存管理笔记源码学习内存管理笔记源码学习基本变量笔记 此文用于汇总跟随陈雷老师及团队的视频,学习源码过程中的思考、整理与心得体会,此文会不断更新 视频传送门:【每日学习记录】使用录像设备记录每天的学习 PHP7...

    Barrior 评论0 收藏0
  • 【Redis5源码学习2019-04-15 简单动态符串SDS

    摘要:关于结构体内存对齐是什么,请参考源码学习内存管理笔记。这说明在当前情况下,字符串结构中的柔性数组的起始位置并不受是否加关键字而影响,是紧跟在结构体后面的,所以节省内存这个说法并不成立。 baiyan 全部视频:https://segmentfault.com/a/11... 今天我们正式进入redis5源码的学习。redis是一个由C语言编写、基于内存、单进程、可持久化的Key-Va...

    Vixb 评论0 收藏0
  • PHP源码学习2019-03-12 PHP基本变量笔记

    摘要:中以表示所有的变量,它是一个结构体。类型是一个联合体,共占用。字段表示字符串的哈希值,在数组的中有用,方便快速定位。字段表示字符串长度这里就是一个柔性数组,在等源码中也被大量使用。 baiyan 全部视频:https://segmentfault.com/a/11... 源视频地址:http://replay.xesv5.com/ll/24... 引入及基本概念 变量本质上就是给一段...

    Fundebug 评论0 收藏0

发表评论

0条评论

blair

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<