字符串管理:zend_string
任何程序都需要管理字符串。在这里,我们将详细介绍适合 PHP 需求的自定义解决方案:zend_string
。每次 PHP 需要使用字符串时,都会使用 zend_string
结构。该结构仅仅是 C 语言的 char *
字符串类型的简单精简包装。
它添加了内存管理的功能,所以同一字符串可以在多个地方共享,而无需重复。另外,一些字符串是“内部的”,即“持久的”分配,并通过内存管理特殊管理,以便它们不会在多个请求中被销毁。之后,那些从Zend 内存管理获得永久分配。
相关学习推荐:PHP编程从入门到精通
结构和访问宏
这里是简单的zend_string
结构:
struct _zend_string { zend_refcounted_h gc; zend_ulong h; size_t len; char val[1]; };
如你所见,该结构嵌入了一个 zend_refcounted_h
标头。这个是内存管理和引用需要用到的。 由于该字符串很有可能作为哈希表检查的关键字,因此它在 h
字段中嵌入了其哈希值。这是无符号长整型 zend_ulong
。仅在需要对 zend_string
进行哈希处理时会用到,特别是和哈希表:zend_array一起用时。这很有可能。
如你所知,字符串知道其长度为 len
字段,以支持“二进制字符串。二进制字符串是嵌入一个或多个 NUL
字符( )的字符串。当传递给库函数,那些字符串会被截断,否则无法正确计算其长度。所以在 zend_string
中,字符串的长度总是已知的。请注意,该长度计算的 ASCII 字符(字节),不计算最后的NUL
,而是计算最终的中间的 NUL。例如,字符串 “foo” 在 zend_string
中存储为 “foo ”,且它的长度为3。另外,字符串 “foo bar” 将存储为 “foo bar ”,且其长度为7。
最终,该字符存储在 char[1]
。这不是 char *
,而是 char[1]
。为什么?这是一种称为 “C struct hack” 的内存优化(你可以使用带有这些术语的搜索引擎)。基本上,它允许引擎为 zend_string
结构和要存储的字符分配空间,作为一个单独的 C 指针。这优化了内存,因为内存访问将是一个连续分配的块,而不是两个分散的块(一个用于存储 zend_string *
,另一个用于存储 char *
)。
必须记住这种 struct hack,由于内存布局看起来像 C 字符位于 C zend_string
结构的末尾,因此当使用 C 调试器(或调试字符串)时可能会感觉到/看到过。该 hack 是完全由 API 管理,当你操作 zend_string
结构时会用到。
使用 zend_string API
简单用例
像 Zvals,你不需要手动操作 zend_string
内部字段,而总是为此使用宏。还存在触发字符串操作的宏。这并不是函数,而是宏,都存储在必需的 Zend/zend_string.h 头文件:
zend_string *str; str = zend_string_init("foo", strlen("foo"), 0); php_printf("This is my string: %sn", ZSTR_VAL(str)); php_printf("It is %zd char longn", ZSTR_LEN(str)); zend_string_release(str);
上面简单的例子为你展示了基本的字符串管理。应该为 zend_string_init()
函数(实际上是宏,但先让我们忽略它)给出完整的 char *
C 字符串和它的长度。类型为 int 的最后一个参数应该为 0 或 1。如果传递0,则要求引擎通过 Zend 内存管理使用请求绑定的堆分配。这种分配在当前请求结束后时销毁。如果你不这么做,则在调试版本中,引擎会提醒你内存泄漏。如果传递1,则要求了所谓的“持久”分配,引擎将使用传统的 C malloc()
调用,并且不会以任何方式追踪内存分配。
注意
如果你需要