K& R的strlen()
只需几行。
int strlen(char *s)
{
char *p = s;
while (*p != '\0')
p++;
return p - s;
}
但是glibc version要长得多。为简单起见,我删除了所有注释和64位实现,提取的版本strlen()
如下所示:
size_t strlen(const char *str)
{
const char *char_ptr;
const unsigned long int *longword_ptr;
unsigned long int longword, magic_bits, himagic, lomagic;
for (char_ptr = str; ((unsigned long int) char_ptr
& (sizeof (longword) - 1)) != 0; ++char_ptr)
if (*char_ptr == '\0')
return char_ptr - str;
longword_ptr = (unsigned long int *) char_ptr;
himagic = 0x80808080L;
lomagic = 0x01010101L;
for (;;)
{
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;
}
}
}
借助非常有用的评论(点击here),我了解了大部分内容。 glibc '\0'
不是逐字节地检查strlen()
,而是检查每个字(32位机器中的4个字节,64位机器中的8个字节)。这样,当字符串相对较长时,可以提高性能。
它通过逐字节读取来检查前几个字符,直到char_ptr
在longword
边界上对齐。然后它使用一个循环来检查longword
是否有任何全零的字节。如果有,检查哪个字节,并返回结果。
我没有得到的部分是,如何检查longword
中的一个字节是全零?
if (((longword - lomagic) & himagic) != 0)
我可以构建longword
0x81818181
的{{1}}值,这可能会使0x81818181 - 0x01010101) & 0x80808080
不等于0
,但是没有全零字节。
这与ASCII值的范围从0
到127
有关,所以0x81
不是有效的ASCII吗?但我不认为C标准强制字符串使用ASCII。
答案 0 :(得分:16)
我明白了。简直不敢相信,我花了半个多小时就完成了它。
检查
是可以的if (((longword - lomagic) & himagic) != 0)
让0x81818181
之类的值通过,因为如果它通过,则每个字节的后续测试都不会返回,因为没有全零字节。因此循环可以继续测试下一个longword
。
支票背后的算法基于Determine if a word has a zero byte
unsigned int v;
bool hasZeroByte = ~((((v & 0x7F7F7F7F) + 0x7F7F7F7F) | v) | 0x7F7F7F7F);
在2的补码中,- 0x01010101
与+ 0xFEFEFEFF
具有相同的效果。不同之处在于glibc没有v & 0x7F7F7F7F
,这可以确保单词中的字节具有最高有效位0
。这可以防止像0x81818181
这样的例子,但glibc省略了它,因为它不必像前面所述那样让它通过。只要它不会错过任何具有全零字节的单词,检查是正确的。