【发布时间】:2013-11-30 00:37:24
【问题描述】:
来自 K&R 的 strlen() 只需要几行代码。
int strlen(char *s)
{
char *p = s;
while (*p != '\0')
p++;
return p - s;
}
但是glibc version 要长得多。为简单起见,我删除了所有的 cmets 和 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 strlen() 不是逐字节检查'\0',而是检查每个字(32 位机器中为 4 个字节,64 位机器中为 8 个字节)。这样,当字符串比较长的时候,可以提高性能。
它通过逐字节读取来检查前几个字符,直到char_ptr 与longword 边界对齐。然后它使用一个循环来检查longword 是否有任何全零字节。如果有,检查哪个字节,并返回结果。
我不明白的部分是,这如何检查longword 中的一个字节是否全为零?
if (((longword - lomagic) & himagic) != 0)
我可以构建一个0x81818181 的longword 值,它可以使0x81818181 - 0x01010101) & 0x80808080 不等于0,但是没有全零字节。
这是否与 ASCII 值范围从 0 到 127 的事实有关,所以 0x81 不是有效的 ASCII?但我不认为 C 标准强制字符串使用 ASCII。
【问题讨论】:
-
Related Why does glibc's strlen need to be so complicated to run quickly? 有一个解决类型双关语 UB 的答案,并链接到 glibc 的手写 asm,它实际上在大多数平台上使用。
-
您的链接似乎已损坏。这是我能找到的另一个github.com/lattera/glibc/blob/master/string/strlen.c
标签: c string performance glibc