strlen高效实现
来源:互联网 发布:刘知白山水画 编辑:程序博客网 时间:2024/05/23 14:40
http://blog.csdn.net/lonelysky/article/details/6614422
高效实现
(1) 一次判断一个字符直到内存对齐,如果在内存对齐之前就遇到'\0'则直接return,否则到(2);
(2) 一次读入并判断一个WORD,如果此WORD中没有为0的字节,则继续下一个WORD,否则到(3);
(3) 到这里则说明WORD中至少有一个字节为0,剩下的就是找出第一个为0的字节的位置然后return。
为了便于下面的讨论,这里假设所用的计算机为32位,即一个WORD为4个字节。下面给出在32位计算机上的C语言实现(假设unsigned long为4个字节):
- typedef unsigned long ulong;
- size_t strlen_c(const char * str) {
- const char * char_ptr;
- const ulong * longword_ptr;
- register ulong longword, magic_bits;
- for (char_ptr = str; ((ulong) char_ptr & (sizeof(ulong) - 1)) != 0;
- ++char_ptr) {
- if (*char_ptr == '\0')
- return char_ptr - str;
- }
- longword_ptr = (ulong*) char_ptr;
- magic_bits = 0x7efefeffL;
- while (1) {
- longword = *longword_ptr++;
- if ((((longword + magic_bits) ^ ~longword) & ~magic_bits) != 0) {
- const char *cp = (const char*) (longword_ptr - 1 );
- if (cp[0] == 0)
- return cp - str;
- if (cp[1] == 0)
- return cp - str + 1;
- if (cp[2] == 0)
- return cp - str + 2;
- if (cp[3] == 0)
- return cp - str + 3;
- }
- }
- }
上面给出的C语言实现虽然不算特别复杂,但也值得花点时间来弄清楚
- for (char_ptr = str; ((ulong) char_ptr & (sizeof(ulong) - 1)) != 0;
- ++char_ptr) {
- if (*char_ptr == '\0')
- return char_ptr - str;
- }
第14行将longword_ptr指向数据对齐后的首地址
- longword_ptr = (ulong*) char_ptr;
第15行给magic_bits赋值(在后面会解释这个值的意义)
- magic_bits = 0x7efefeffL;
第17行读入一个WORD到longword并将longword_ptr指向下一个WORD
- longword = *longword_ptr++;
- if ((((longword + magic_bits) ^ ~longword) & ~magic_bits) != 0)
(1) longword + magic_bits
其中magic_bits的二进制表示如下:
b3 b2 b1 b0
31------------------------------->0
magic_bits: 01111110 11111110 11111110 11111111
magic_bits中的31,24,16,8这些bits都为0,我们把这几个bits称为holes,注意在每个byte的左边都有一个hole。31------------------------------->0
magic_bits: 01111110 11111110 11111110 11111111
检测0字节:
如果longword 中有一个字节的所有bit都为0,则进行加法后,从这个字节的右边的字节传递来的进位都会落到这个字节的最低位所在的hole上,而从这个字节的最高位则永远不会产生向左边字节的hole的进位。则这个字节左边的hole在进行加法后不会改变,由此可以检测出0字节;相反,如果longword中所有字节都不为0,则每个字节中至少有1位为1,进行加法后所有的hole都会被改变。
为了便于理解,请看下面的例子:
b3 b2 b1 b0
31------------------------------->0
longword: XXXXXXXX XXXXXXXX 00000000 XXXXXXXX
+ magic_bits: 01111110 11111110 11111110 11111111
上面longword中的b1为0,X可能为0也可能为1。因为b1的所有bit都为0,而从b0传递过来的进位只可能是0或1,很显然b1永远也不会产生进位,所以加法后longword的第16 bit这个hole不会变。31------------------------------->0
longword: XXXXXXXX XXXXXXXX 00000000 XXXXXXXX
+ magic_bits: 01111110 11111110 11111110 11111111
(2) ^ ~longword
这一步取出加法后longword中所有未改变的bit。
(3) & ~magic_bits
最后取出longword中未改变的hole,如果有任何hole未改变则说明longword中有为0的字节。
根据上面的描述,如果longword中有为0的字节,则if中的表达式结果为非0,否则为0。
如果b3为10000000,则进行加法后第31 bit这个hole不会变,这说明我们无法检测出b3为10000000的所有WORD。值得庆幸的是用于strlen的字符串都是ASCII标准字符,其值在0-127之间,这意味着每一个字节的第一个bit都为0。因此上面的算法是安全的。
一旦检测出longword中有为0的字节,后面的代码只需要找到第一个为0的字节并返回相应的长度就OK
- const char *cp = (const char*) (longword_ptr - 1 );
- if (cp[0] == 0)
- return cp - str;
- if (cp[1] == 0)
- return cp - str + 1;
- if (cp[2] == 0)
- return cp - str + 2;
- if (cp[3] == 0)
- return cp - str + 3;
另一种实现
- size_t strlen_d(const char *str) {
- const char *char_ptr;
- const ulong *longword_ptr;
- register ulong longword, himagic, lomagic;
- for (char_ptr = str; ((ulong) char_ptr & (sizeof(ulong) - 1)) != 0;
- ++char_ptr) {
- if (*char_ptr == '\0')
- return char_ptr - str;
- }
- longword_ptr = (ulong*) char_ptr;
- himagic = 0x80808080L;
- lomagic = 0x01010101L;
- while (1) {
- longword = *longword_ptr++;
- if (((longword - lomagic) & himagic) != 0) {
- const char *cp = (const char*) (longword_ptr - 1);
- if (cp[0] == 0)
- return cp - str;
- if (cp[1] == 0)
- return cp - str + 1;
- if (cp[2] == 0)
- return cp - str + 2;
- if (cp[3] == 0)
- return cp - str + 3;
- }
- }
- }
magic_bits换成了himagic和lomagic
- himagic = 0x80808080L;
- lomagic = 0x01010101L;
以及 if语句变得比较简单了
- if (((longword - lomagic) & himagic) != 0)
(1) longword - lomagic
himagic和lomagic的二进制表示如下:
b3 b2 b1 b0
31------------------------------->0
himagic: 10000000 10000000 10000000 10000000
lomagic: 00000001 00000001 00000001 00000001
31------------------------------->0
himagic: 10000000 10000000 10000000 10000000
lomagic: 00000001 00000001 00000001 00000001
在这种方法中假设所有字符都是ASCII标准字符,其值在0-127之间,因此longword总是如下形式:
b3 b2 b1 b0
31------------------------------->0
longword: 0XXXXXXX 0XXXXXXX 0XXXXXXX 0XXXXXXX
检测0字节:31------------------------------->0
longword: 0XXXXXXX 0XXXXXXX 0XXXXXXX 0XXXXXXX
如果longword 中有一个字节的所有bit都为0,则进行减法后,这个字节的最高位一定会从0变为1;相反,如果longword中所有字节都不为0,则每个字节中至少有1位为1,进行减法后这个字节的最高位依然为0。
(2) & himagic
这一步取出每个字节最高位的1,如果有任意字节最高位为1则说明longword中有为0的字节。
根据上面的描述,如果longword中有为0的字节,则if中的表达式结果为非0,否则为0。
0 0
- strlen高效实现
- strlen高效实现
- strlen高效实现
- strlen 的高效实现,充分利用位运算
- strlen为何如此高效
- 高效的strlen函数
- strlen 实现
- strlen实现
- strlen()实现
- strlen 实现
- 实现 strlen
- strlen实现
- 微软strlen函数实现
- 内联汇编 实现 strlen
- glibc strlen 实现
- 实现strlen()函数
- strlen函数的实现
- strlen函数的实现
- 2014-04-19学习笔记
- rkhunter 脚本学习记录(二)
- db2 断开连接几种方法的比较
- DB2表空间的静默状态
- hdu-1166 敌兵布阵 树状数组
- strlen高效实现
- hdu 1166-敌兵布阵 线段树
- Servlet线程安全
- [LeetCode 21] Merge Two Sorted Lists Solution
- 微软笔试第8道选择题
- Android开发者必备的42个链接
- 开源java企业网站建设系统MiinCMP1.0发布!
- Ruby on Rails /MAC 下安装报错 unknown argument: '-multiply_definedsuppress'
- 通用Makefile