如何将 char* 与 C 中的字符串文字进行比较?

How to compare char* to string literal in C?

我需要将一些 char *(我知道其长度)与一些字符串文字进行比较。现在我是这样做的:

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

问题是我有很多这样的比较要进行,分解并键入每个比较非常乏味。而且,这样做维护起来很困难,而且容易引入错误。

我的问题是是否有 shorthand 可以输入这个(也许是 MACRO)。

我知道有 strncmp 并且我已经看到 GCC optimizes it。所以,如果 shorthand 是使用 strncmp,像这样:

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

然后,我想知道第二个示例与第一个示例具有相同(或更好)的性能。

是的,会的。 但是,您的代码并未将 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一旦看到不相等的字符就可以停止。而且它绝对只需要读取到最小字符串的末尾。

您的示例暗示您的字符串始终以 NUL 结尾。在那种情况下,不要费心提前获取它们的长度,因为这涉及到搜索 NUL。相反,你可以做

memcmp(str, "OK", 3);

这样,NUL 也得到比较。如果您的长度 > 2,则结果将 > 0,如果长度较短,则结果将 < 0。

这是一个函数调用,memcmp几乎可以保证比您手写的代码优化得更好。同时,除非您发现这段代码是瓶颈,否则不要费心去优化。还要记住,我在我的机器上 运行 的任何基准测试不一定适用于你的。

进行此更改的唯一真正原因是为了提高可读性。