strlen 的高效实现,充分利用位运算

来源:互联网 发布:淘宝男装潮牌店铺排行 编辑:程序博客网 时间:2024/05/11 12:47

处理字符串的时候,经常需要获得字符串的长度,C标准库提供了strlen这个函数,
实现一个普通的版本并不困难,大致上跟下面的代码类似。

挨个判断字符是否为0,遇到0则退出,代码很简洁,也不算性能低。只是有点不足,
在字长是4字节或者8字节的计算机上,每次只读取一个字节,有些浪费计算机的能力,
如果每次都读取4字节或者8字节,总的读取次数就大大减少,在读取4字节或者8字节
的时候,如果地址不在边界上,机器就要分两次才能读取完成,这样性能将会降低,
弱化优化效果,所以前几个字符必须单独处理,然后从字长边界地址开始,每次读取
4字节或者8字节。

新的方式:

  • 开头的几字节单独处理
  • 中间部分以4字节或8字节为单位处理
  • 最后几字节单独处理

看上去很好,但是还有一个问题,4字节或者8字节读取的时候,如何保证有全0的字节
存在,因为0是用来表示字符串的结尾的。判断连续的几个字节中是否存在全0的字节,
成了优化的关键。我们不能一个字节一个字节判断,因为优化的思想就是一次读取多个
字节,减少总的读取次数,单独判断每一个字节的话,就失去优化的效果了。

怎么办呢,当然首先考虑位运算了。

  • 一个纯0的字节有什么特点? 很明显,每一位都是0,按位取反后每一位都是1。
  • 一个全0的字节还有什么特点? 这个字节减1,必然要从更高字节借1,借1后,
    该字节的最高位必然是1。

似乎有些眉目了,以4字节整数n为例,我们只要把每个字节分别减去1,如果有纯0
的字节存在,必然会有借位,借位之后会在字节最高 位留下一个1。只要判断每个
字节的最高位是否存在1就可以了,然而,这里还有一个问题,就是这个4字节整数
里,某些字节本来最高 位可能就含有1,所以必须排除掉这些字节。

解决方案:

  • 将n的每字节分别减1,再取每字节最高位,得x,借位的字节最高位一定是1
  • 将n的每字节按位取反后取最高位,得y,n中最高位是0的字节,在y中该位是1
  • 将x和y按位与运算,若不为0,则n存在某字节最高位不是1,但在借位时变1了

若n中存在全0字节,则 x&y 一定不为0,因为借位的那个字节最高位会被置为1;
若n中不存在全0字节,则不会产生借位,x&y 等于0。写出公式如下:
x&y ==  (n-0x01010101) & ~n & 0x80808080

如果是标准ASCII字符,每字节最高位一定是0,此时只考虑借位即可,不必考虑原本
最高位就是1的情况,用 x&y ==  (n-0x01010101) & 0x8080808 就可以了。

以下是glibc中strcat的实现,基于以上结论,就不难理解了。
注意:glibc还考虑了字长是8字节的情况。