Glibc中strlen的实现
glibc不是一个字符一个字符地检测是否是0,而是以四个字节为单位进行检测,首先将扫描指针定位到字符串所在的最后四字节处,然后高效地判断四个字节处是否为0
1 size_tstrlen (str)
2 const char *str;
3 {
4 const char *char_ptr;
5 const unsigned long int *longword_ptr;
6 unsigned long int longword, himagic, lomagic;
7
8 /* Handle the first few characters by reading one character at a time.
9 Do this until CHAR_PTR is aligned on a longword boundary. */
10 for (char_ptr = str; ((unsigned long int) char_ptr
11 & (sizeof (longword) - 1)) != 0;
12 ++char_ptr)
13 if (*char_ptr == '\0')
14 return char_ptr - str;
15
16 /* All these elucidatory comments refer to 4-byte longwords,
17 but the theory applies equally well to 8-byte longwords. */
18
19 longword_ptr = (unsigned long int *) char_ptr;
20
21 /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits
22 the "holes." Note that there is a hole just to the left of
23 each byte, with an extra at the end:
24
25 bits: 01111110 11111110 11111110 11111111
26 bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
27
28 The 1-bits make sure that carries propagate to the next 0-bit.
29 The 0-bits provide holes for carries to fall into. */
30 himagic = 0x80808080L;
31 lomagic = 0x01010101L;
32 if (sizeof (longword) > 4)
33 {
34 /* 64-bit version of the magic. */
35 /* Do the shift in two steps to avoid a warning if long has 32 bits. */
36 himagic = ((himagic << 16) << 16) | himagic;
37 lomagic = ((lomagic << 16) << 16) | lomagic;
38 }
39 if (sizeof (longword) > 8)
40 abort ();
41
42 /* Instead of the traditional loop which tests each character,
43 we will test a longword at a time. The tricky part is testing
44 if *any of the four* bytes in the longword in question are zero. */
45 for (;;)
46 {
47 longword = *longword_ptr++;
48
49 if (((longword - lomagic) & ~longword & himagic) != 0)
50 {
51 /* Which of the bytes was the zero? If none of them were, it was
52 a misfire; continue the search. */
53
54 const char *cp = (const char *) (longword_ptr - 1);
55
56 if (cp[0] == 0)
57 return cp - str;
58 if (cp[1] == 0)
59 return cp - str + 1;
60 if (cp[2] == 0)
61 return cp - str + 2;
62 if (cp[3] == 0)
63 return cp - str + 3;
64 if (sizeof (longword) > 4)
65 {
66 if (cp[4] == 0)
67 return cp - str + 4;
68 if (cp[5] == 0)
69 return cp - str + 5;
70 if (cp[6] == 0)
71 return cp - str + 6;
72 if (cp[7] == 0)
73 return cp - str + 7;
74 }
75 }
76 }
77 }
代码注释比较清楚,主要看两大块内容:
1)10~14行
str的起始地址有可能不是字节对齐的(即str有可能从一个字的第二个字节处开始),就像代码中的注释一样,10~14行的代码处理字节对齐前的一些字符,当char_ptr行走到字节对齐的位置时退出for循环。在这个过程中,如果遇到了'\0',则输出字符串长度。
2)30~31行设置了两个魔数,然后判断如果机器是64位的话做相应的调整
3)45行开始的for循环,每次以一个机器字(32位机器上是4个字节)为单位判断这个字中是否存在某个字节是0,如果存在值为0的某字节,则找出这个字节,返回长度,否则,继续判断下一个机器字。
49行判断一个机器字中是否存在某个为0的字节,下面以32位机器为例详细看看哈。
if (((longword - lomagic) & ~longword & himagic) != 0)
魔数lomagic和himagic在32位机器上是下面的样子
b3 b2 b1 b0
31----------------------------------->0
lomagic 00000001 00000001 00000001 00000001
himagic 10000000 10000000 10000000 10000000
longword 0XXXXXXX 0XXXXXXX 0XXXXXXX 0XXXXXXX
a, longword-lomagic 如果longword中某一个字节为0,则longword-lomagic之后该位的最高位肯定为1(为什么?因为-lomagic就相当于+0xFEFEFEFF),如果每个字节中的XXXXXXX中至少有一个1,则longword-lomagic之后最高为肯定是0。所以longword-lomagic是判断longword中是否存在0的。
b, ~longword & himagic 如果longword中存在某个字节的最高位是1,则~longword & himagic的结果是1
综合上面的分析,如果longword中存在某个字节的范围不在[1~127]之间,则if条件判断为真。这说明在这个字中字符串结束了,接着我们在这个字中寻找值为'\0'的字符然后返回长度即可。