2017-04-24 45 views
1

我見過的標準執行的使用strlen的指針:çstrlen的使用指針

int strlen(char * s) { 
    char *p = s; 
    while (*p!='\0') 
    p++; 
    return p-s; 
} 

我得到這個工作,但是當我嘗試這樣做使用3種方法(學習指針運算現在)做的,我想知道他們最近怎麼了?

  1. 這有點類似於這本書。這是錯的嗎?

    int strlen(char * s) { 
        char *p = s; 
        while (*p) 
        p++; 
        return p-s; 
    } 
    
  2. 我雖然這將是錯的,如果我傳遞一個空字符串,但仍然給我0,因爲p有點混亂預增:(現在它回到我5)

    int strlen(char * s) { 
        char *p = s; 
        while (*++p) 
        ; 
        return p-s; 
    } 
    
  3. 想通了,後增加和返回+1。

    int strlen(char * s) { 
        char *p = s; 
        while (*p++) 
        ; 
        return p-s; 
    } 
    

回答

1

1)看起來好像沒什麼問題。我個人比較喜歡'\ 0'的明確比較,所以很明顯,你不打算在例如上下文不清楚的情況下將p與NULL指針進行比較。

2)當程序運行時,稱爲堆棧的內存區域未初始化。局部變量居住在那裏。您編寫程序的方式將p放入堆棧中(如果您製作了const或使用了malloc,它幾乎肯定會在其他地方存在)。當你看到*p時會發生什麼,然後你偷看了堆棧。如果字符串長度爲0,則與char p[1] = {0}相同。預遞增查看\0之後的字節,因此您正在查看未定義的內存。這裏是龍!

3)我不認爲這裏有一個問題:)正如你看到的,它總是返回一個比正確的答案。

附錄:您也可以使用一個for循環,如果你喜歡這種風格寫:

size_t strlen(char * s) { 
    char *p = s; 
    for (; *p != '\0'; p++) {} 
    return p - s; 
} 

或者(更容易出錯-LY)

size_t strlen(char * s) { 
    char *p = s; 
    for (; *p != '\0'; p++); 
    return p - s; 
} 

而且,strlen的能不會返回負數,所以您應該使用無符號值。 size_t更好。

+0

這是我的主要看起來像和現在,這是給我5. main(){0} {0} {0} {char A [] =「」; printf(「%d」,strlen(A)); } – user7703770

+0

啊......我明白了。發生什麼事情是你在檢查指針前先增加指針。你最終檢查的是堆棧中的數據。由於沒有定義,你可能會得到空字符串的任何值,直到你點擊一個\ 0。 – lungj

0

版本1是精 - while (*p != '\0')相當於while (*p != 0),這相當於while (*p)

在原始代碼和版本1,指針p如果且僅當*p0(IOW,你是不是在字符串的結尾)前進。

版本2和3預先p不管*p是否0與否*p++評價爲字符p指向,並且作爲副作用高級p*++p評價爲之後的字符p指向p,並且副作用爲p。因此,版本2和3將始終在字符串末尾超前p,這就是爲什麼您的值已關閉。

-1

當您比較strlen替換函數的性能時遇到的一個問題是,與長字符串的實際strlen函數相比,它們的性能會受到影響嗎?爲什麼? strlen在搜索字符串的結尾時每次迭代處理多個字節。你如何實現更高效的替代?

這並不困難。基本的方法是每次迭代查看4個字節,並根據在這4個字節中發現的空字節的位置調整返回值。你可以做類似如下(使用數組索引):

size_t strsz_idx (const char *s) { 
    size_t len = 0; 
    for(;;) { 
     if (s[0] == 0) return len; 
     if (s[1] == 0) return len + 1; 
     if (s[2] == 0) return len + 2; 
     if (s[3] == 0) return len + 3; 
     s += 4, len += 4; 
    } 
} 

您可以使用指針和麪具做同樣的事情:

size_t strsz (const char *s) { 
    size_t len = 0; 
    for(;;) { 
     unsigned x = *(unsigned*)s; 
     if((x & 0xff) == 0) return len; 
     if((x & 0xff00) == 0) return len + 1; 
     if((x & 0xff0000) == 0) return len + 2; 
     if((x & 0xff000000) == 0) return len + 3; 
     s += 4, len += 4; 
    } 
} 

無論哪種方式,你會發現一個4字節的比較每次迭代都會爲您提供相當於strlen本身的性能。

+0

第一個循環在當時不檢查4個字節。它使用更復雜的循環,比原來有更多的分支和附加。第二循環更糟糕:它**違反別名規則**並導致*未定義行爲*。此外,這個答案不回答這個問題。 Downvoted。 – user694733

+0

沒有什麼比關於嚴格別名規則的小知識更糟糕了。關於'鑄造和來自char *'的規則是什麼?這裏沒有違規行爲,如果這是你的丁的基礎,你是100%錯誤的。你爲什麼不用'-Wall -Wextra -pedantic'編譯代碼並找出答案?當我錯了時,我不介意冷靜下來,但當我正確的時候,我也不會期待一個。 –

+0

允許將任何對象類型的地址轉換爲char *,這是標準中的特定異常。但是,除非原始類型和最終類型匹配('unsigned * - > char * - > unsigned *'是合法的,'char [] - > unsigned *'不是),否則不能執行反向和取消引用。 N1570 6.5 p7列出了可能的別名類型,並且在這種情況下6個都不適用(甚至不是最後一個)。測試不足以檢查C語言中的某些內容是否合法,因爲它可能*看起來*有時會起作用。 '*(unsigned *)s'就是這樣一個典型的例子。 – user694733