學(xué)習(xí)高效編程的有效途徑之一就是閱讀高手寫(xiě)的源代碼,CRT(C/C++ Runtime Library)作為底層的函數(shù)庫(kù),實(shí)現(xiàn)必然高效。恰好手中就有g(shù)libc和VC的CRT源代碼,于是挑了一個(gè)相對(duì)簡(jiǎn)單的函數(shù)strlen研究了一下,并對(duì)各種實(shí)現(xiàn)作了簡(jiǎn)單的效率測(cè)試。

strlen的函數(shù)原形如下:

      size_t strlen(const char *str);

strlen返回str中字符的個(gè)數(shù),其中str為一個(gè)以'\0'結(jié)尾的字符串(a null-terminated string)。

1. 簡(jiǎn)單實(shí)現(xiàn)
如果不管效率,最簡(jiǎn)單的實(shí)現(xiàn)只需要4行代碼:

1 size_t strlen_a(const char * str) {
2     size_t length = 0
;
3     while (*str++
)
4         ++
length;
5     return
 length;
6 }

也許可以稍加改進(jìn)如下:

1 size_t strlen_b(const char * str) {
2     const char *cp =
 str;
3     while (*cp++
)
4 
        ;
5     return (cp - str - 1
);
6 }

2. 高效實(shí)現(xiàn)
很顯然,標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)肯定不會(huì)如此簡(jiǎn)單,上面的strlen_a以及strlen_b都是一次判斷一個(gè)字符直到發(fā)現(xiàn)'\0'為止,這是非常低效的。比較高效的實(shí)現(xiàn)如下(在這里WORD表示計(jì)算機(jī)中的一個(gè)字,不是WORD類(lèi)型):
(1) 一次判斷一個(gè)字符直到內(nèi)存對(duì)齊,如果在內(nèi)存對(duì)齊之前就遇到'\0'則直接return,否則到(2);
(2) 一次讀入并判斷一個(gè)WORD,如果此WORD中沒(méi)有為0的字節(jié),則繼續(xù)下一個(gè)WORD,否則到(3);
(3) 到這里則說(shuō)明WORD中至少有一個(gè)字節(jié)為0,剩下的就是找出第一個(gè)為0的字節(jié)的位置然后return。


NOTE:
數(shù)據(jù)對(duì)齊(data alignment),是指數(shù)據(jù)所在的內(nèi)存地址必須是該數(shù)據(jù)長(zhǎng)度的整數(shù)倍,這樣CPU的存取速度最快。比如在32位的計(jì)算機(jī)中,一個(gè)WORD為4 byte,則WORD數(shù)據(jù)的起始地址能被4整除的時(shí)候CPU的存取效率比較高。CPU的優(yōu)化規(guī)則大概如下:對(duì)于n字節(jié)(n = 2,4,8...)的元素,它的首地址能被n整除才能獲得最好的性能。

為了便于下面的討論,這里假設(shè)所用的計(jì)算機(jī)為32位,即一個(gè)WORD為4個(gè)字節(jié)。下面給出在32位計(jì)算機(jī)上的C語(yǔ)言實(shí)現(xiàn)(假設(shè)unsigned long為4個(gè)字節(jié)):

 1 typedef unsigned long  ulong;
 2 

 3 size_t strlen_c(const char * str) {
 4 

 5     const char * char_ptr;
 6     const ulong *
longword_ptr;
 7 
    register ulong longword, magic_bits;
 8 

 9     for (char_ptr =  str; ((ulong)char_ptr 
10         & (sizeof(ulong) - 1)) != 0
;
11         ++
char_ptr) {
12         if (*char_ptr == '\0'
)
13             return char_ptr -
 str;
14 
    }
15 

16     longword_ptr = (ulong* )char_ptr;
17 

18     magic_bits = 0x7efefeffL ;
19 

20     while (1 ) {
21 

22         longword = *longword_ptr++ ;
23 

24         if ((((longword + magic_bits) ^ ~longword) & ~magic_bits) != 0 ) {
25 

26             const char *cp = (const char*)(longword_ptr - 1 );
27 
            
28             if (cp[0== 0
)
29                 return cp -
 str;
30             if (cp[1== 0
)
31                 return cp - str + 1
;
32             if (cp[2== 0
)
33                 return cp - str + 2
;
34             if (cp[3== 0
)
35                 return cp - str + 3
;
36 
        }
37 
    }
38 }

3. 源碼剖析
上面給出的C語(yǔ)言實(shí)現(xiàn)雖然不算特別復(fù)雜,但也值得花點(diǎn)時(shí)間來(lái)弄清楚,先看9-14行:
for (char_ptr = str; ((ulong)char_ptr & (sizeof(ulong) - 1)) != 0++char_ptr) {
    
if (*char_ptr == '\0'
)
        
return char_ptr -
 str;
}

上面的代碼實(shí)現(xiàn)了數(shù)據(jù)對(duì)齊,如果在對(duì)齊之前就遇到'\0'則可以直接return char_ptr - str;

第16行將longword_ptr指向數(shù)據(jù)對(duì)齊后的首地址

longword_ptr = (ulong*)char_ptr;

第18行給magic_bits賦值(在后面會(huì)解釋這個(gè)值的意義)
magic_bits = 0x7efefeffL;

第22行讀入一個(gè)WORD到longword并將longword_ptr指向下一個(gè)WORD
longword = *longword_ptr++;

第24行的if語(yǔ)句是整個(gè)算法的核心,該語(yǔ)句判斷22行讀入的WORD中是否有為0的字節(jié)
if ((((longword + magic_bits) ^ ~longword) & ~magic_bits) != 0)
if語(yǔ)句中的計(jì)算可以分為如下3步:
(1) longword + magic_bits
其中magic_bits的二進(jìn)制表示如下:
                  b3      b2       b1       b0
              
31------------------------------->0

  magic_bits: 
01111110 11111110 11111110 11111111
magic_bits中的31,24,16,8這些bits都為0,我們把這幾個(gè)bits稱(chēng)為holes,注意在每個(gè)byte的左邊都有一個(gè)hole。

檢測(cè)0字節(jié):
如果longword 中有一個(gè)字節(jié)的所有bit都為0,則進(jìn)行加法后,從這個(gè)字節(jié)的右邊的字節(jié)傳遞來(lái)的進(jìn)位都會(huì)落到這個(gè)字節(jié)的最低位所在的hole上,而從這個(gè)字節(jié)的最高位則永遠(yuǎn)不會(huì)產(chǎn)生向左邊字節(jié)的hole的進(jìn)位。則這個(gè)字節(jié)左邊的hole在進(jìn)行加法后不會(huì)改變,由此可以檢測(cè)出0字節(jié);相反,如果longword中所有字節(jié)都不為0,則每個(gè)字節(jié)中至少有1位為1,進(jìn)行加法后所有的hole都會(huì)被改變。

為了便于理解,請(qǐng)看下面的例子:
                  b3      b2       b1       b0
              31------------------------------->0
  longword:   XXXXXXXX XXXXXXXX 
00000000 XXXXXXXX
+ magic_bits: 01111110 11111110 11111110 11111111
上面longword中的b1為0,X可能為0也可能為1。因?yàn)閎1的所有bit都為0,而從b0傳遞過(guò)來(lái)的進(jìn)位只可能是0或1,很顯然b1永遠(yuǎn)也不會(huì)產(chǎn)生進(jìn)位,所以加法后longword的第16 bit這個(gè)hole不會(huì)變。

(2)  ^ ~longword
這一步取出加法后longword中所有未改變的bit。

(3)  & ~magic_bits
最后取出longword中未改變的hole,如果有任何hole未改變則說(shuō)明longword中有為0的字節(jié)。

根據(jù)上面的描述,如果longword中有為0的字節(jié),則if中的表達(dá)式結(jié)果為非0,否則為0。
NOTE:
如果b3為10000000,則進(jìn)行加法后第31 bit這個(gè)hole不會(huì)變,這說(shuō)明我們無(wú)法檢測(cè)出b3為10000000的所有WORD。值得慶幸的是用于strlen的字符串都是ASCII標(biāo)準(zhǔn)字符,其值在0-127之間,這意味著每一個(gè)字節(jié)的第一個(gè)bit都為0。因此上面的算法是安全的。

一旦檢測(cè)出longword中有為0的字節(jié),后面的代碼只需要找到第一個(gè)為0的字節(jié)并返回相應(yīng)的長(zhǎng)度就OK:
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;

4. 另一種實(shí)現(xiàn)
 1 size_t strlen_d(const char *str) {
 2 

 3     const char *char_ptr;
 4     const ulong *
longword_ptr;
 5 
    register ulong longword, himagic, lomagic;
 6 

 7     for (char_ptr = str; ((ulong)char_ptr 
 8         & (sizeof(ulong) - 1)) != 0
;
 9         ++
char_ptr) {
10         if (*char_ptr == '\0'
)
11             return char_ptr -
 str;
12 
    }
13 

14     longword_ptr = (ulong*)char_ptr;
15 

16     himagic = 0x80808080L;
17     lomagic = 0x01010101L
;
18 

19     while (1) {
20 

21         longword = *longword_ptr++;
22 

23         if (((longword - lomagic) & himagic) != 0) {
24 

25             const char *cp = (const char*)(longword_ptr - 1);
26 
            
27             if (cp[0== 0
)
28                 return cp -
 str;
29             if (cp[1== 0
)
30                 return cp - str + 1
;
31             if (cp[2== 0
)
32                 return cp - str + 2
;
33             if (cp[3== 0
)
34                 return cp - str + 3
;
35 
        }
36 
    }
37 }
上面的代碼與strlen_c基本一樣,不同的是:
magic_bits換成了himagic和lomagic
himagic = 0x80808080L;
lomagic 
= 0x01010101L;
以及 if語(yǔ)句變得比較簡(jiǎn)單了
if (((longword - lomagic) & himagic) != 0)

if語(yǔ)句中的計(jì)算可以分為如下2步:
(1) longword - lomagic
himagic和lomagic的二進(jìn)制表示如下:
                b3      b2       b1       b0
            
31------------------------------->0

  himagic:  10000000 10000000 
10000000 10000000
  lomagic:  00000001 00000001 00000001 00000001

在這種方法中假設(shè)所有字符都是ASCII標(biāo)準(zhǔn)字符,其值在0-127之間,因此longword總是如下形式:
                b3      b2       b1       b0
            
31------------------------------->0

  longword: 
0XXXXXXX 0XXXXXXX 0XXXXXXX 0XXXXXXX

檢測(cè)0字節(jié):
如果longword 中有一個(gè)字節(jié)的所有bit都為0,則進(jìn)行減法后,這個(gè)字節(jié)的最高位一定會(huì)從0變?yōu)?;相反,如果longword中所有字節(jié)都不為0,則每個(gè)字節(jié)中至少有1位為1,進(jìn)行減法后這個(gè)字節(jié)的最高位依然為0。

 (2)  & himagic
這一步取出每個(gè)字節(jié)最高位的1,如果有任意字節(jié)最高位為1則說(shuō)明longword中有為0的字節(jié)。

根據(jù)上面的描述,如果longword中有為0的字節(jié),則if中的表達(dá)式結(jié)果為非0,否則為0。

5. 匯編實(shí)現(xiàn)
VC CRT的匯編實(shí)現(xiàn)與前面strlen_c算法類(lèi)似

  1         page    ,132
  2         title   strlen - return the length of a null-terminated string
  3 ;***

  4 ;strlen.asm - contains strlen() routine
  5 
;
  6 
;       Copyright (c) Microsoft Corporation. All rights reserved.
  7 
;
  8 
;Purpose:
  9 ;       strlen returns the length of a null-
terminated string,
 10 ;       not including the null byte
 itself.
 11 
;
 12 ;*******************************************************************************

 13 
 14         .xlist
 15 
        include cruntime.inc
 16 
        .list
 17 

 18 page
 19 ;***

 20 ;strlen - return the length of a null-terminated string
 21 
;
 22 
;Purpose:
 23 
;       Finds the length in bytes of the given string, not including
 24 ;       the final null
 character.
 25 
;
 26 
;       Algorithm:
 27 ;       int strlen (const char *
 str)
 28 
;       {
 29 ;           int length = 0
;
 30 
;
 31 ;           while*str++
 )
 32 ;                   ++
length;
 33 
;
 34 ;           return
( length );
 35 
;       }
 36 
;
 37 
;Entry:
 38 ;       const char * str -
 string whose length is to be computed
 39 
;
 40 
;Exit:
 41 ;       EAX = length of the string "str", exclusive of the final null byte

 42 ;
 43 
;Uses:
 44 
;       EAX, ECX, EDX
 45 
;
 46 
;Exceptions:
 47 
;
 48 ;*******************************************************************************

 49 
 50         CODESEG
 51 

 52         public  strlen
 53 

 54 strlen  proc \
 55         buf:ptr byte

 56 
 57         OPTION PROLOGUE:NONE, EPILOGUE:NONE
 58 

 59         .FPO    ( 010000 )
 60 

 61 string  equ     [esp + 4]
 62 

 63         mov     ecx,string              ; ecx -> string
 64         test    ecx,3                   ; test if string is aligned on 32
 bits
 65         je      short
 main_loop
 66 

 67 str_misaligned:
 68         ; simple byte
 loop until string is aligned
 69         mov     al,byte
 ptr [ecx]
 70         add     ecx,1

 71         test    al,al
 72         je      short
 byte_3
 73         test    ecx,3

 74         jne     short str_misaligned
 75 

 76         add     eax,dword ptr 0         ; 5 byte nop to align label below
 77 

 78         align   16                      ; should be redundant
 79 

 80 main_loop:
 81         mov     eax,dword ptr [ecx]     ; read 4
 bytes
 82 
        mov     edx,7efefeffh
 83 
        add     edx,eax
 84         xor     eax,-1

 85         xor     eax,edx
 86         add     ecx,4

 87         test    eax,81010100h
 88         je      short
 main_loop
 89         ; found zero byte
 in the loop
 90         mov     eax,[ecx - 4
]
 91         test    al,al                   ; is it byte 0

 92         je      short byte_0
 93         test    ah,ah                   ; is it byte 1

 94         je      short byte_1
 95         test    eax,00ff0000h           ; is it byte 2

 96         je      short byte_2
 97         test    eax,0ff000000h          ; is it byte 3

 98         je      short byte_3
 99         jmp     short main_loop         ; taken if bits 24-30
 are clear and bit
100                                         ; 31
 is set
101 

102 byte_3:
103         lea     eax,[ecx - 1
]
104 
        mov     ecx,string
105 
        sub     eax,ecx
106 
        ret
107 
byte_2:
108         lea     eax,[ecx - 2
]
109 
        mov     ecx,string
110 
        sub     eax,ecx
111 
        ret
112 
byte_1:
113         lea     eax,[ecx - 3
]
114 
        mov     ecx,string
115 
        sub     eax,ecx
116 
        ret
117 
byte_0:
118         lea     eax,[ecx - 4
]
119 
        mov     ecx,string
120 
        sub     eax,ecx
121 
        ret
122 

123 strlen  endp
124 

125         end

6. 測(cè)試結(jié)果
為了對(duì)上述各種實(shí)現(xiàn)的效率有一個(gè)大概的認(rèn)識(shí),我在VC8和GCC下分別進(jìn)行了測(cè)試,測(cè)試時(shí)均采用默認(rèn)優(yōu)化方式。下面是在GCC下運(yùn)行幾百萬(wàn)次后的結(jié)果(在VC8下的運(yùn)行結(jié)果與此相似):
strlen_a
--------------------------------------------------

       
1:        515 ticks         0.515 seconds
       
2:        375 ticks         0.375
 seconds
       
3:        375 ticks         0.375
 seconds
       
4:        375 ticks         0.375
 seconds
       
5:        375 ticks         0.375
 seconds
   total:       
2015 ticks         2.015
 seconds
 average:        
403 ticks         0.403
 seconds
--------------------------------------------------


strlen_b
--------------------------------------------------
       
1:        360 ticks          0.36 seconds
       
2:        390 ticks          0.39
 seconds
       
3:        375 ticks         0.375
 seconds
       
4:        360 ticks          0.36
 seconds
       
5:        375 ticks         0.375
 seconds
   total:       
1860 ticks          1.86
 seconds
 average:        
372 ticks         0.372
 seconds
--------------------------------------------------


strlen_c
--------------------------------------------------
       
1:        187 ticks         0.187 seconds
       
2:        172 ticks         0.172
 seconds
       
3:        187 ticks         0.187
 seconds
       
4:        187 ticks         0.187
 seconds
       
5:        188 ticks         0.188
 seconds
   total:        
921 ticks         0.921
 seconds
 average:        
184 ticks        0.1842
 seconds
--------------------------------------------------


strlen_d
--------------------------------------------------
       
1:        172 ticks         0.172 seconds
       
2:        187 ticks         0.187
 seconds
       
3:        172 ticks         0.172
 seconds
       
4:        187 ticks         0.187
 seconds
       
5:        188 ticks         0.188
 seconds
   total:        
906 ticks         0.906
 seconds
 average:        
181 ticks        0.1812
 seconds
--------------------------------------------------


strlen
--------------------------------------------------
       
1:        187 ticks         0.187 seconds
       
2:        172 ticks         0.172
 seconds
       
3:        188 ticks         0.188
 seconds
       
4:        172 ticks         0.172
 seconds
       
5:        187 ticks         0.187
 seconds
   total:        
906 ticks         0.906
 seconds
 average:        
181 ticks        0.1812
 seconds
--------------------------------------------------

源代碼:點(diǎn)擊下載

posted on 2007-10-12 13:19 螞蟻終結(jié)者 閱讀(5133) 評(píng)論(29)  編輯 收藏 引用 所屬分類(lèi): The Annotated CRT Sources

Feedback

# re: strlen源碼剖析 2007-09-26 19:29 msdn47

果然很快,效率高啊  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-26 21:29 hi

(2) ^ ~longword
這一步取出加法后longword中所有未改變的bit。

======================================

這個(gè)什么意思?  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 08:19 螞蟻終結(jié)者

可能我說(shuō)的不夠清楚,看下面的例子:
                   b3      b2       b1       b0
               31------------------------------->0
     longword: 00001001 00011000 00000000 00001100
+  magic_bits: 01111110 11111110 11111110 11111111
          sum: 10001000 00010110 11111111 00001011
^   ~longword: 11110110 11100111 11111111 11110011
            a: 01111110 11110001 00000000 11111000
& ~magic_bits: 10000001 00000001 00000001 00000000
       result: 00000000 00000001 00000000 00000000
sum = longword + magic_bits;
a = sum ^ ~longword;
即用sum與longword逐位比較,如果有某個(gè)位相同,就說(shuō)這個(gè)位在加法后未改變,這樣在a中為1的位就是未改變的。
 
result = a & ~magic_bits;
得到未改變的hole位,從上例可以看出第16 bit這個(gè)hole加法后未改變,這樣就檢測(cè)出了0字節(jié)。
 
  回復(fù)  更多評(píng)論   

 

# re: strlen源碼剖析 2007-09-27 08:28 wangmuy

贊啊!以前不懂的今天終于看懂了!  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析[未登錄](méi) 2007-09-27 08:42 漂舟

樓主剖析得全面,頂 !  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 08:52 Yong Sun

只能檢測(cè)0~127,是個(gè)問(wèn)題,最好能兼容0~255,有部分制表符和擴(kuò)展字符位于這個(gè)區(qū)域。  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 09:06 螞蟻終結(jié)者

實(shí)際上0~255都能檢測(cè)出來(lái)的:
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;
如果上面的語(yǔ)句執(zhí)行完還沒(méi)有return,則會(huì)繼續(xù)下一次循環(huán),這樣還是能檢測(cè)到在if語(yǔ)句中漏掉的128~255,只不過(guò)效率上會(huì)有所損失。如果要檢測(cè)0~255之間的字符,strlen_c比strlen_d要好。因?yàn)閟trlen_c只會(huì)漏掉這樣的WORD:
    10000000 XXXXXXXX XXXXXXXX XXXXXXXX
  回復(fù)  更多評(píng)論   

 

# re: strlen源碼剖析 2007-09-27 09:23 k120

為了便于下面的討論,這里假設(shè)所用的計(jì)算機(jī)為32位,即一個(gè)WORD為4個(gè)字節(jié)?呵呵,筆誤吧?
  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 09:35 螞蟻終結(jié)者

Sorry,我不該用WORD這個(gè)單詞。我說(shuō)的WORD在這里表示計(jì)算機(jī)中的一個(gè)字長(zhǎng),不是一般為2個(gè)字節(jié)的WORD類(lèi)型。
  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 10:32 Yong Sun

另外,是否有endian的問(wèn)題呢?  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 10:39 Yong Sun

想了想,應(yīng)該沒(méi)有,呵呵  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 11:16 螞蟻終結(jié)者

c語(yǔ)言的版本不會(huì)有endian的問(wèn)題,如果用匯編就需要注意了。
假設(shè)有4個(gè)連續(xù)的字節(jié)abcd,現(xiàn)在要找出其中的第一個(gè)0字節(jié):
1. 在PowerPC這種big-endian的計(jì)算機(jī)上,將這4個(gè)字節(jié)讀到寄存器中依然是
abcd,從左到右找到最左邊的0字節(jié)就OK了。

2. 在X86這種little-endian的計(jì)算機(jī)上,將這4個(gè)字節(jié)讀到寄存器中就會(huì)變成
dcba,這就需要從右到左找到最右邊的0字節(jié)。

可以看出,上面VC的匯編實(shí)現(xiàn)是針對(duì)X86計(jì)算機(jī)的。
  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-09-27 12:51 ∑x

如果能做到這樣的分析,還有什么能學(xué)不會(huì)?!  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-10-12 16:36 Minidx全文檢索

幾天前看過(guò)的文章怎么又跑前面來(lái)了?!  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-10-12 16:43 螞蟻終結(jié)者

我也奇怪,就改了一下,再發(fā)布就變了。連日期都變了,郁悶。。。  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-10-12 17:04 Minidx全文檢索

不過(guò)是好文章,多看幾遍也不錯(cuò)~  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-10-13 09:24 erran

強(qiáng)!希望這樣的好文不要鏈接失效!o(∩_∩)o...  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-11-02 13:04 really green

for (char_ptr = str; ((ulong)char_ptr
& (sizeof(ulong) - 1)) != 0 ;
++ char_ptr) {
if (*char_ptr == '\0' )
return char_ptr - str;
}

我比較菜,這里就看不明白:
((ulong)char_ptr
& (sizeof(ulong) - 1)) != 0 ;

(ulong)char_ptr 這個(gè)轉(zhuǎn)換把一個(gè) char * 轉(zhuǎn)成 ulong 會(huì)發(fā)生什么事情?  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-11-02 19:28 really green

想了想這個(gè)問(wèn)題問(wèn)得挺幼稚,我理解 (ulong)char_ptr應(yīng)該得到一個(gè)ulong的值,這個(gè)值就是指針char_ptr的值。  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-11-04 09:35 螞蟻終結(jié)者

you gotta it!  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-12-04 05:39 福福

如果有中文字符,這個(gè)是不是有問(wèn)題
if (((longword - lomagic) & himagic) != 0)  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-12-05 19:39 螞蟻終結(jié)者

@福福
中文字符應(yīng)該用wcslen才對(duì),strlen是用來(lái)處理單字節(jié)字符串的。
詳細(xì)描述請(qǐng)看MSDN:
However, strlen interprets the string as a single-byte character string, so its return value is always equal to the number of bytes, even if the string contains multibyte characters. wcslen is a wide-character version of strlen; the argument of wcslen is a wide-character string and the count of characters is in wide (two-byte) characters.
  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-12-27 14:01 Fox

幾年沒(méi)動(dòng)過(guò)匯編了,看了代碼,有種耳目一新的感覺(jué)~~~~~~~~  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2007-12-31 17:44 Sunky

感謝博主的精彩剖析。
感覺(jué)到我不會(huì)的東西太多了
由此堅(jiān)定了我看GNU C的決心
謝謝  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2008-09-07 16:11 star

博主的文章允許轉(zhuǎn)載么?
昨天看glibc里的注釋覺(jué)得云里霧里
今天看了博主的文章,突然覺(jué)得豁然開(kāi)朗啊 ;-)
還有想問(wèn)一個(gè)問(wèn)題 glibc里是這樣處理64位的
if (sizeof (longword) > 4)
{
/* 64-bit version of the magic. */
/* Do the shift in two steps to avoid a warning if long has 32 bits. */
magic_bits = ((0x7efefefeL << 16) << 16) | 0xfefefeffL;
himagic = ((himagic << 16) << 16) | himagic;
lomagic = ((lomagic << 16) << 16) | lomagic;
}
為什么不一次就移32位呢?  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2008-09-07 19:54 螞蟻終結(jié)者

@star
歡迎轉(zhuǎn)載,轉(zhuǎn)載當(dāng)然是沒(méi)有問(wèn)題的,畢竟寫(xiě)文章就是能讓更多的人看到!

為什么不一次就移32位呢?
我也不太清楚,可能就其中注釋所說(shuō):
/* Do the shift in two steps to avoid a warning if long has 32 bits. */
只是為了避免warn吧呵呵!  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2008-11-07 11:42 test

似乎CRT庫(kù)的這種寫(xiě)法有點(diǎn)越界訪(fǎng)問(wèn)的意思,呵呵。  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2009-03-18 17:08 123

@福福
strlen算法在處理中文時(shí)不會(huì)出問(wèn)題,關(guān)鍵點(diǎn)在后面的if(cp[?]==0)。
不過(guò)處理中文時(shí)效率會(huì)跟最簡(jiǎn)單的strlen一樣。  回復(fù)  更多評(píng)論   

# re: strlen源碼剖析 2009-04-29 14:32 uestc

分析得很清楚,寫(xiě)得也很詳細(xì),贊一個(gè)。  回復(fù)  更多評(píng)論