我正在審查一些代碼,我看到有人做了的strcmp爲空字符串
if (0 == strcmp(foo,""))
我很好奇,因爲我認爲這將是快做
if (foo[0] == '\0')
這是正確的或者是strcmp優化足以使它們相同。
(我意識到,即使有一定的差異會很小,但我想你節省至少幾說明,使用我的方法。)
我正在審查一些代碼,我看到有人做了的strcmp爲空字符串
if (0 == strcmp(foo,""))
我很好奇,因爲我認爲這將是快做
if (foo[0] == '\0')
這是正確的或者是strcmp優化足以使它們相同。
(我意識到,即使有一定的差異會很小,但我想你節省至少幾說明,使用我的方法。)
你說得對:由於調用strcmp()
將堆棧管理和內存跳轉添加到實際的strcmp指令中,您只需檢查字符串的第一個字節即可獲得一些說明。
對於你的好奇心,你可以檢查的strcmp()代碼在這裏:(!我想代碼將充滿#ifdef
和晦澀__GNUSOMETHING
,但它實際上是相當簡單)http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h=bd53c05c6e21130b091bd75c3fc93872dd71fe4b;hb=HEAD
的strcmp()是一個函數調用,因而具有函數調用開銷。 foo [0]是直接訪問數組,所以它顯然更快。
訪問一個數組在執行時間是1階,所以它比函數快。
這是微觀優化,因爲它會得到,但我想如果您在索引foo之前添加空檢查(除非您知道它永遠不會爲空),它在技術上會節省函數調用的開銷。
我想你錯過了這一點。 OP不檢查空指針,而是檢查零長度字符串。因爲效果是相同的,所以執行直接檢查和'strcmp'調用永遠沒有任何目的。 – 2011-06-01 22:05:10
@ R我相信你應該重新閱讀我的回覆。我只是簡單地說他應該在盲目索引它之前對指針執行一個空檢查。我沒有說關於調用strcmp的任何信息。 – 2011-06-01 22:17:14
在這種情況下,我沒有看到使用strcmp的優勢。編譯器我很聰明,可以優化它,但它不會比直接檢查'\ 0'字節更快。這個實現者可能選擇了這個構造,因爲他認爲它更具可讀性,但我認爲這是一個在這種情況下的味道問題。雖然我會寫支票有點不同,因爲這是,這似乎是最常用於檢查一個空字符串的成語:
if(!*str)
和
if(*str)
檢查非空字符串。
是的,我喜歡那樣更好,我工作的地方雖然我們有一些關於在條件....中明確的嚴格指導,所以即使它沒有形成很多意義,我也必須明確地檢查'\ 0'或0 – Pablitorun 2011-06-01 15:58:15
這顯然會更快,這可能是值得把自己的代碼中的內聯函數,或者甚至是一個宏,如果你打算用它前進:
int isEmpty(const char *string)
{
return ! *string;
}
int isNotEmpty(const char *string)
{
return *string;
}
int isNullOrEmpty(const char *string)
{
return string == NULL || ! *string;
}
int isNotNullOrEmpty(const char *string)
{
return string != NULL && *string;
}
,讓編譯器爲你優化。無論如何,strcmp
需要最終檢查'\0'
,所以你總是至少與它相等。 (老實說,我可能會讓編譯器優化上面的內部共享,例如,isEmpty
可能會翻轉
一個好的優化編譯器可能會優化掉函數調用,然後從內聯函數中消除循環。你的方法沒有辦法變慢,儘管有可能它的速度是一樣的。
+1到Gui13提供了一個鏈接到gcc stdlib源代碼strcmp(http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h = bd53c05c6e21130b091bd75c3fc93872dd71fe4b; HB = HEAD)!
你是正確的,strcmp永遠不會比直接比較更快[1],但問題是,編譯器是否會優化它?我很害怕嘗試去衡量,但我對它的容易程度感到驚喜。我的示例代碼(忽略標題):
bool isEmpty(char * str) {
return 0==std::strcmp(str,"");
}
bool isEmpty2(char * str) {
return str[0]==0;
}
,我試圖在編譯,先用gcc -S -o- emptystrcmptest.cc
,然後用gcc -S -O2 -o- emptystrcmptest.cc
。令我驚喜的是,雖然我無法很好地閱讀該程序集,但非優化版本清楚地顯示了差異,並且優化版本清楚地顯示了兩個函數產生相同的程序集。
所以,我認爲一般來說,沒有必要擔心這個優化級別。
如果您爲嵌入式系統使用編譯器,並且知道它不處理這種簡單的優化(或根本沒有標準庫),請使用手動編碼的特例版本。
如果你正在編碼,使用更可讀的版本(imho可能是strcmp或strlen或[0] == 0取決於上下文)。
如果您正在編寫高效的代碼,您希望每秒調用數千次或數百萬次,(a)實際上更高效的測試,以及(b)如果可讀版本實際上太慢,請嘗試編寫一些指令這將編譯更好的程序集。
隨着gcc -S -o- emptystrcmptest.cc
:
.file "emptystrcmptest.cc"
.section .rdata,"dr"
LC0:
.ascii "\0"
.text
.align 2
.globl __Z7isEmptyPc
.def __Z7isEmptyPc; .scl 2; .type 32; .endef
__Z7isEmptyPc:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $LC0, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call _strcmp
movl %eax, -4(%ebp)
cmpl $0, -4(%ebp)
sete %al
movzbl %al, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
ret
.align 2
.globl __Z8isEmpty2Pc
.def __Z8isEmpty2Pc; .scl 2; .type 32; .endef
__Z8isEmpty2Pc:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
cmpb $0, (%eax)
sete %al
movzbl %al, %eax
popl %ebp
ret
emptystrcmptest.cc:10:2: warning: no newline at end of file
.def _strcmp; .scl 2; .type 32; .endef
隨着gcc -S -O2 -o- emptystrcmptest.cc
:
.file "emptystrcmptest.cc"
emptystrcmptest.cc:10:2: warning: no newline at end of file
.text
.align 2
.p2align 4,,15
.globl __Z7isEmptyPc
.def __Z7isEmptyPc; .scl 2; .type 32; .endef
__Z7isEmptyPc:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
popl %ebp
cmpb $0, (%eax)
sete %al
movzbl %al, %eax
ret
.align 2
.p2align 4,,15
.globl __Z8isEmpty2Pc
.def __Z8isEmpty2Pc; .scl 2; .type 32; .endef
__Z8isEmpty2Pc:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
popl %ebp
cmpb $0, (%eax)
sete %al
movzbl %al, %eax
ret
[1]雖然要小心 - 在情況下比針對零,庫和編譯器代碼的直接測試任何更復雜的通常會比手工製作的代碼更好。
+1 @Jack這給了@Brandon Moretz的評論很大的信心。好帖子。在純可讀性方面,我仍然不確定我喜歡哪種方式,但這對於過早優化來說絕對是一個很好的觀點。 – pickypg 2011-06-03 15:48:56
你是對的質疑模式,我更喜歡你的方式。 – SingleNegationElimination 2011-06-01 14:50:16
當檢查空字符串時,對我做'if(strlen(foo)== 0)'更有意義。 – 2011-06-01 14:50:42
@Jamie Wong:如果字符串長度爲1Meg,那麼在查找終止空值之前,您必須檢查100萬字節;當你只對字符串有零或多於零字符的情況感興趣時,這是一個非常大的損失。 – SingleNegationElimination 2011-06-01 14:52:17