Как сравнить char * со строковым литералом в C? - PullRequest
0 голосов
/ 09 июля 2020

Мне нужно сравнить некоторые символы * (длину которых я знаю) с некоторыми строковыми литералами. Прямо сейчас я делаю это так:

void do_something(char * str, int len) {
  if (len == 2 && str[0] == 'O' && str[1] == 'K' && str[2] == '\0') {
    // do something...
  }
}

Проблема в том, что у меня есть много подобных сравнений, и довольно утомительно разбивать их и вводить каждое из этих сравнений. Кроме того, это сложно поддерживать и легко вводить ошибки.

Мой вопрос в том, есть ли сокращение для этого (возможно, МАКРОС).

Я знаю, что есть strncmp, и я видел, что G CC оптимизирует его . Итак, если в сокращении используется strncmp, например:

void do_something(char * str, int len) {
  if (len == 2 && strncmp(str, "OK", len) == 0) {
    // do something...
  }
}

Тогда я хотел бы знать, что второй пример имеет такую ​​же (или лучшую) производительность, что и первый.

Ответы [ 2 ]

1 голос
/ 10 июля 2020

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

memcmp(str, "OK", 3);

. Таким образом, NUL тоже сравниваются. Если ваша длина> 2, результат будет> 0, а если он короче, результат будет <0. </p>

Это единственный вызов функции, и memcmp практически гарантированно будет лучше оптимизирован, чем ваш рукописный код. В то же время не беспокойтесь об оптимизации, если вы не обнаружите, что этот код является узким местом. Также имейте в виду, что любой тест, который я запускаю на своей машине, не обязательно будет применяться к вашему.

Единственная реальная причина для внесения этого изменения - для удобства чтения.

1 голос
/ 09 июля 2020

Да, будет. Однако ваш код не сравнивает char * со строковым литералом. Это сравнение двух строковых литералов. Компилятор достаточно умен, чтобы заметить это и оптимизировать весь код. Остается только код внутри блока if.

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

cc -S -std=c11 -pedantic -O3 test.c

Сначала с вашим исходным кодом ...

#include <stdio.h>
#include <string.h>

int main() {
    unsigned int len = 2;
    char * str = "OK";
    if (len == 2 && strncmp(str, "OK", len) == 0) {
      puts("Match");
    }
}

Затем с помощью puts.

#include <stdio.h>
#include <string.h>

int main() {
    //unsigned int len = 2;
    //char * str = "OK";
    //if (len == 2 && strncmp(str, "OK", len) == 0) {
      puts("Match");
    //}
}

Два файла сборки практически одинаковы. Никаких следов струн не осталось, только puts.

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14    sdk_version 10, 14
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    leaq    L_.str(%rip), %rdi
    callq   _puts
    xorl    %eax, %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "Match"


.subsections_via_symbols

Это плохое место для оптимизации. Сравнение строк с небольшими строками вряд ли будет проблемой производительности.

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

Тогда как strncmp может остановиться, как только увидит неравные символы. И он определенно должен читать только до конца самой маленькой строки.

...