glibc strlen()实现如何工作

时间:2013-11-16 16:51:13

标签: c string performance glibc

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_ptrlongword边界上对齐。然后它使用一个循环来检查longword是否有任何全零的字节。如果有,检查哪个字节,并返回结果。

我没有得到的部分是,如何检查longword中的一个字节是全零?

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

我可以构建longword 0x81818181的{​​{1}}值,这可能会使0x81818181 - 0x01010101) & 0x80808080不等于0,但是没有全零字节。

这与ASCII值的范围从0127有关,所以0x81不是有效的ASCII吗?但我不认为C标准强制字符串使用ASCII。

1 个答案:

答案 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省略了它,因为它不必像前面所述那样让它通过。只要它不会错过任何具有全零字节的单词,检查是正确的。