字符串处理函数
2013-10-22 17:04
141 查看
这篇文章中我想讨论下strlen()的问题。strlen()的作用是计算一个字符串的长度,最简单的代码如下:
好像这是理所当然的事情,以前从来没有想过这种实现方式的效率问题,也没有想过strlen()是否还有其他实现方式,直到看到glibc的代码。glibc中strlen()是这么实现的:
是不是很复杂?这种实现方法的思想是每次处理多个字符(32位系统中每次处理4个字符,64位系统中每次处理8个字符),通过这种改进strlen()的效率得到了大幅提升。为了支持64位系统,strlen()中增加了一些代码,我们先去掉这些代码,只关注32位系统。代码可以分成两段,第一段代码如下:
这段代码在处理字符串对齐问题。因为字符串str的起始地址不一定按照4字节对齐了,而strlen()每次处理4字节,因此当字符串地址按4字节对齐后可以加快处理效率,因此这段代码先处理str开头的几个字符(最多循环3次)。如果头3个字符中就找到了结束符'\0',那么strlen()就结束了。
第二段代码如下:
这段代码以四字节为单位在字符串中查找结束符'\0',如果在某次循环中找到了结束符,那么就判断第几个字符是结束符,cp[0]表示第0个字符,cp[1]表示第1个字符,cp[2]表示第2个字符,cp[3]表示第3个字符。那么,现在的重点就是如何判断四字节构成的unsigned
long int类型变量是否包含结束符,这是通过(longword - lomagic) & ~longword & himagic判断的。如果这个结果不为0说明longword包含结束符,循环结束。下面详细分析这个判断过程。
首先看longword - lomagic,longword长度是4字节,lomagic的值是0x01010101,因此longword-lomagic对longword中每个字节减1。如果longword某个字节的值为0或者大于0x80,那么longword-lomagic中对应字节的最高位为1,否则对应字节最高位为0。再看~longword&himagic,himagic的值是0x80808080,~longword表示对longword取反操作。如果longword中某个字节大于等于0x80,那么~longword&himagic中对应字节的最高位为0;否则~longword&himagic中对应字节的最高位为1。因此(longword
- lomagic) & (~longword & himagic)将前面两种情况进行与运算取二者的交集,也就是(0 或者 大于0x80)&(小于0x80),交集只有等于0一种情况。也就是说,上述表达式只在longword中至少一个字节为0的条件下成立。通过这种方法就可以快速判断longword中是否包含结束符。
最后我们对strlen()的速率进行测试。定义了一个长度为100000字节的字符串,然后调用两种算法计算字符串长度。原始方法花费了277微秒,优化后的方法花费了95微秒,可见优化效果还是很明显。
size_t strlen(const char *str) { const char *s; for (s = str; *s; ++s) ; return (s - str); }
好像这是理所当然的事情,以前从来没有想过这种实现方式的效率问题,也没有想过strlen()是否还有其他实现方式,直到看到glibc的代码。glibc中strlen()是这么实现的:
size_t strlen (str) const char *str; { const char *char_ptr; const unsigned long int *longword_ptr; unsigned long int longword, himagic, lomagic; /* Handle the first few characters by reading one character at a time. Do this until CHAR_PTR is aligned on a longword boundary. */ for (char_ptr = str; ((unsigned long int) char_ptr & (sizeof (longword) - 1)) != 0; ++char_ptr) if (*char_ptr == '\0') return char_ptr - str; /* All these elucidatory comments refer to 4-byte longwords, but the theory applies equally well to 8-byte longwords. */ longword_ptr = (unsigned long int *) char_ptr; himagic = 0x80808080L; lomagic = 0x01010101L; if (sizeof (longword) > 4) { /* 64-bit version of the magic. */ /* Do the shift in two steps to avoid a warning if long has 32 bits. */ himagic = ((himagic << 16) << 16) | himagic; lomagic = ((lomagic << 16) << 16) | lomagic; } if (sizeof (longword) > 8) abort (); /* Instead of the traditional loop which tests each character, we will test a longword at a time. The tricky part is testing if *any of the four* bytes in the longword in question are zero. */ for (;;) { longword = *longword_ptr++; if (((longword - lomagic) & ~longword & himagic) != 0) { /* Which of the bytes was the zero? If none of them were, it was a misfire; continue the search. */ 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; if (sizeof (longword) > 4) { if (cp[4] == 0) return cp - str + 4; if (cp[5] == 0) return cp - str + 5; if (cp[6] == 0) return cp - str + 6; if (cp[7] == 0) return cp - str + 7; } } } }
是不是很复杂?这种实现方法的思想是每次处理多个字符(32位系统中每次处理4个字符,64位系统中每次处理8个字符),通过这种改进strlen()的效率得到了大幅提升。为了支持64位系统,strlen()中增加了一些代码,我们先去掉这些代码,只关注32位系统。代码可以分成两段,第一段代码如下:
for (char_ptr = str; ((unsigned long int) char_ptr & (sizeof (longword) - 1)) != 0; ++char_ptr) if (*char_ptr == '\0') return char_ptr - str;
这段代码在处理字符串对齐问题。因为字符串str的起始地址不一定按照4字节对齐了,而strlen()每次处理4字节,因此当字符串地址按4字节对齐后可以加快处理效率,因此这段代码先处理str开头的几个字符(最多循环3次)。如果头3个字符中就找到了结束符'\0',那么strlen()就结束了。
第二段代码如下:
longword_ptr = (unsigned long int *) char_ptr; himagic = 0x80808080L; lomagic = 0x01010101L; for (;;) { longword = *longword_ptr++; if (((longword - lomagic) & ~longword & 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; } }
这段代码以四字节为单位在字符串中查找结束符'\0',如果在某次循环中找到了结束符,那么就判断第几个字符是结束符,cp[0]表示第0个字符,cp[1]表示第1个字符,cp[2]表示第2个字符,cp[3]表示第3个字符。那么,现在的重点就是如何判断四字节构成的unsigned
long int类型变量是否包含结束符,这是通过(longword - lomagic) & ~longword & himagic判断的。如果这个结果不为0说明longword包含结束符,循环结束。下面详细分析这个判断过程。
首先看longword - lomagic,longword长度是4字节,lomagic的值是0x01010101,因此longword-lomagic对longword中每个字节减1。如果longword某个字节的值为0或者大于0x80,那么longword-lomagic中对应字节的最高位为1,否则对应字节最高位为0。再看~longword&himagic,himagic的值是0x80808080,~longword表示对longword取反操作。如果longword中某个字节大于等于0x80,那么~longword&himagic中对应字节的最高位为0;否则~longword&himagic中对应字节的最高位为1。因此(longword
- lomagic) & (~longword & himagic)将前面两种情况进行与运算取二者的交集,也就是(0 或者 大于0x80)&(小于0x80),交集只有等于0一种情况。也就是说,上述表达式只在longword中至少一个字节为0的条件下成立。通过这种方法就可以快速判断longword中是否包含结束符。
最后我们对strlen()的速率进行测试。定义了一个长度为100000字节的字符串,然后调用两种算法计算字符串长度。原始方法花费了277微秒,优化后的方法花费了95微秒,可见优化效果还是很明显。
相关文章推荐
- T-SQL字符串处理函数
- 字符串处理函数——提取字符串中的字符(重要)
- 实现常用字符串处理函数(不调用库函数)
- VC中的字符串处理函数
- mysql 字符串处理函数 截取字符串
- php字符串处理函数大全
- php字符串处理函数大全
- php字符串处理函数大全
- sqlserver 字符串处理函数解释
- 字符串处理函数strcat 、 strcpy 、 strlen、strcmp的源函数
- JS中字符串的处理函数
- Python内置的字符串处理函数
- sql 处理字符串的函数有哪些?
- c语言中对字符串进行处理的函数
- Php 字符串处理函数大全
- Python内置的字符串处理函数整理
- oracle函数大全-字符串处理函数
- 在delphi中判断字符串是否数字,以及精度处理函数
- 字符串处理函数
- oracle 字符串处理instr()函数使用