glibc-strlen 分析

strlen 函数的参数

strlen 以一个字符型指针为参数,使用 const 修饰符保护字符串的值不被改变。

strlen 函数的常见实现

strlen 函数的一个常见实现是依次遍历每个字符,如果不为 ‘\0’,则给计数变量加 1,遇到了 ‘\0’ 则返回计数变量的值。这种实现方式当字符串长度很短时有很好的性能,当字符串较长时就有点捉襟见肘了。

常见实现引发的思考

如果将上述常见实现作为一个天花板,它的短板在于处理长字符串时的不足,这个不足的关键又在于扫描长度为一个字符的限制。如果能够一次扫描多个元素,那么在计数长字符串时就能够得到比常见实现更好的性能。

从一次扫描一个元素到一次扫描多个元素,扫描次数的减少将带来更好的性能。这样的思考让我联想到了中断触发频率问题、read or write 读写单元大小问题、tcp/ip 中的 low-water 问题。这几个问题有着相同的特点。

strlen 中每次扫描字符的个数相当于中断触发的频率、读写单元大小、low-water 大小。strlen 相较于其它问题而言没有太复杂的前期过程,仅仅需要传递一个字符型指针为参数。在这种环境下,提高 strlen 函数每次扫描字符的数量所带来的性能提高并不显著。

glibc 中 strlen 函数的实现

glibc 内建的 strlen 首先对字符串的前几个未按照 longword 对齐的地址依次扫描,如果扫描到字符串结尾则返回字符串长度退出。这里使用一个临时的字符指针来保存当前正在扫描的字符地址,该地址与字符串起始地址做差就能得到字符串的长度。

当字符的地址按照 longword 对齐,则以 longword 大小为扫描单位。首先生成 himagic 与 lomagic 掩码,仅支持 longword 长度为 4 字节或 8 字节,其它字长则调用 abort 函数终止整个进程。

制作好掩码后按照 longword 大小扫描待处理的字符串。通过执行下面的代码来测试 longword 中是否有结束字符 ‘\0’。

         if (((longword - lomagic) & ~longword & himagic) != 0)

当发现有 longword 中存在结束字符时, 判断结束字符出现的位置,不存在结束字符则继续扫描下一个 longword 大小的单元。使用下面的代码来获取新的 longword 的值,遍历字符串。

        longword = *longword_ptr++;

longword 中保存的是旧值。当发现 longword 中存在结束字符时,首先将 longword_ptr 减一,使其指向当前 longword 的地址。代码如下:

        const char *cp = (const char *) (longword_ptr - 1);

longword_ptr 先减一然后再转化为 const char * 类型,注意这里的 longword_ptr 为无符号长整型指针类型,对它减一意味着对地址减去一个无符号长整型数据的大小。

这之后依次查询结束字符的位置,找到结束字符在 longword 中的地址,该地址与字符串首地址的差值就是字符串的长度。

对于掩码部分的分析需要进一步思考,现在暂时跳过!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值