чего strcmp на пустую строку

Я просматривал какой-то код, и я видел, как кто-то сделал

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

мне любопытно, потому что я думаю, что было бы быстрее сделать

if (foo[0] == '')

это правильно или strcmp оптимизирован достаточно, чтобы сделать их одинаковыми.

(Я понимаю, что даже если бы была какая-то разница, это было бы мало, но я думаю, что вы сохраните хотя бы несколько инструкций, используя мой метод.)

8 ответов


вы правы: с момента вызова strcmp() добавляет управление стеком и переход памяти к фактическим инструкциям strcmp, вы получите несколько инструкций, просто проверив первый байт вашей строки.

для вашего любопытства вы можете проверить код strcmp() здесь: http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h=bd53c05c6e21130b091bd75c3fc93872dd71fe4b;hb=HEAD

(Я думал, что код будет наполнен #ifdef и непонятных __GNUSOMETHING, но это на самом деле довольно просто!)


strcmp () является вызовом функции и, следовательно, имеет накладные расходы на вызов функции. foo[0] - это прямой доступ к массиву, поэтому он, очевидно, быстрее.


Я не вижу преимуществ использования strcmp в этом случае. Компилятор мой достаточно умен, чтобы оптимизировать его, но он не будет быстрее, чем проверка байта "\0 " напрямую. Исполнитель этого мог бы выбрать эту конструкцию, потому что он думал, что она более читабельна, но я думаю, что в этом случае это вопрос вкуса. Хотя я бы написал чек немного по-другому, так как это идиома, которая, кажется, чаще всего используется для проверки пустой строки:

if( !*str )

и

if( *str )

чтобы проверить на пустую строку.


+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 ""
            .text
            .align 2
    .globl __Z7isEmptyPc
            .def    __Z7isEmptyPc;  .scl    2;      .type   32;     .endef
    __Z7isEmptyPc:
            pushl   %ebp
            movl    %esp, %ebp
            subl    , %esp
            movl    $LC0, 4(%esp)
            movl    8(%ebp), %eax
            movl    %eax, (%esp)
            call    _strcmp
            movl    %eax, -4(%ebp)
            cmpl    , -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    , (%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    , (%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    , (%eax)
        sete    %al
        movzbl  %al, %eax
        ret

[1] хотя будьте осторожны-в случаях, более сложных, чем прямой тест против нуля, код библиотеки и компилятора обычно будет лучше, чем ручной код.


хороший оптимизирующий компилятор может оптимизировать вызов функции, а затем исключить цикл из встроенной функции. Нет никакого способа, чтобы ваш метод мог быть медленнее, хотя есть вероятность, что он будет такой же скоростью.


доступ к массиву-это порядок 1 во времени выполнения, поэтому он быстрее, чем функция.


Это так же микро-оптимизация, как и получается, но я полагаю, что если вы добавили нулевую проверку перед индексированием foo (если вы не знаете, что она никогда не будет нулевой), это технически сэкономит накладные расходы на вызов функции.


Это явно будет быстрее, и это, наверное, стоит поместить свой код в встроенную функцию, или даже макрос, если вы планируете двигаться вперед с этим:

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 необходимо в конечном итоге проверить '' таким образом, вы всегда по крайней мере равны ему. (честно говоря, я бы, вероятно, позволил компилятору оптимизировать внутренний обмен вышеуказанным, например,isEmpty вероятно, просто переверните isNotEmpty)