如何将 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
几乎可以保证比您手写的代码优化得更好。同时,除非您发现这段代码是瓶颈,否则不要费心去优化。还要记住,我在我的机器上 运行 的任何基准测试不一定适用于你的。
进行此更改的唯一真正原因是为了提高可读性。
我需要将一些 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
几乎可以保证比您手写的代码优化得更好。同时,除非您发现这段代码是瓶颈,否则不要费心去优化。还要记住,我在我的机器上 运行 的任何基准测试不一定适用于你的。
进行此更改的唯一真正原因是为了提高可读性。