strcmp为空字符串

时间:2011-06-01 14:47:29

标签: c strcmp

我正在审核一些代码,我看到有人做了

if (0 == strcmp(foo,""))

我很好奇,因为我认为做一个

会更快
if (foo[0] == '\0')

这是正确的还是strcmp足够优化以使它们相同。

(我意识到即使存在一些差异,它也会很小,但是我想通过使用我的方法保存至少一些指令。)

8 个答案:

答案 0 :(得分:10)

你是对的:因为调用strcmp()会增加堆栈管理并将内存跳转到实际的strcmp指令,所以只需检查字符串的第一个字节就可以获得一些指令。

为了您的好奇心,您可以在此处查看strcmp()代码:http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h=bd53c05c6e21130b091bd75c3fc93872dd71fe4b;hb=HEAD

(我认为代码会被#ifdef__GNUSOMETHING填充,但它实际上相当简单!)

答案 1 :(得分:9)

strcmp()是一个函数调用,因此具有函数调用开销。 foo [0]是对数组的直接访问,所以它显然更快。

答案 2 :(得分:5)

在这种情况下,我认为没有使用strcmp的优势。编译器我足够聪明,可以优化它,但它不会比直接检查'\ 0'字节快。这个实现者可能选择了这个结构,因为他认为它更具可读性,但我认为这是一个在这种情况下的品味问题。虽然我会把支票写得有点不同,因为这是最常用来检查空字符串的习语:

if( !*str )

if( *str )

检查非空字符串。

答案 3 :(得分:4)

+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]虽然要小心 - 在比零直接测试更复杂的情况下,库和编译器代码通常会比手工编写的代码更好。

答案 4 :(得分:1)

一个好的优化编译器可能会优化掉函数调用,然后从内联函数中消除循环。你的方法可能没有办法变慢,尽管有可能速度相同。

答案 5 :(得分:0)

执行时对数组的访问顺序为1,因此,它比函数更快。

答案 6 :(得分:0)

这是微优化,但我想如果你在索引foo之前添加了一个空检查(除非你知道它永远不会为null),它在技术上会节省函数调用的开销。

答案 7 :(得分:0)

它显然会更快,如果您打算继续使用它,可能值得将您自己的代码放在内联函数中,甚至可能放在宏中:

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可能只是翻转isNotEmpty