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'的字符然后返回长度即可。

  

posted @ 2012-05-29 23:00  CobbLiu  阅读(1082)  评论(0编辑  收藏  举报