我正在审查一些代码,我看到有人做了的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