strncmp()
函数实际上只有一个用例(用于字典排序):
其中一个字符串具有已知长度, † 另一个字符串已知以 NUL 结尾。(作为奖励,具有已知长度的字符串根本不需要 NUL 终止。
我相信只有一个用例的原因(前缀匹配检测不是字典排序):‡ (1)如果两个字符串都以NUL结尾,则应使用strcmp()
,因为它将正确完成工作; (2)如果两个字符串都有已知的长度,则应使用memcmp()
,因为它将避免基于每个字节的对NUL进行不必要的检查。
我正在寻找一种惯用(且可读)的方式来使用该函数以字典顺序正确比较两个这样的参数(其中一个是 NUL 终止的,其中一个不一定是 NUL 终止的,长度已知)。
成语存在吗?如果是这样,它是什么?如果不是,它应该是什么,或者应该使用什么来代替?
简单地使用strncmp()
的结果是行不通的,因为在已知长度的参数短于 NUL 终止的参数的情况下,它会导致错误的相等结果,并且它恰好是一个前缀。因此,需要额外的代码来测试这种情况。
作为一个独立的函数,我认为这种结构没有太大问题,而且它似乎是惯用的:
/* s1 is NUL terminated */
int variation_as_function (const char *s1, const char *s2, size_t s2len) {
int result = strncmp(s1, s2, s2len);
if (result == 0) {
result = (s1[s2len] != ' ');
}
return result;
}
但是,当将此构造内联到代码中时,当相等需要特殊操作时,它会导致对0
进行双重测试:
int result = strncmp(key, input, inputlen);
if (result == 0) {
result = (key[inputlen] != ' ');
}
if (result == 0) {
do_something();
} else {
do_something_else();
}
内联调用的动机是因为独立函数是深奥的:哪个字符串参数是 NUL 终止的,哪个不是
。请注意,问题不在于性能,而在于编写惯用代码和采用编码风格的最佳实践。我看到比较存在一些 DRY 违规。有没有一种简单的方法来避免重复?
†已知长度,我的意思是长度是正确的(没有嵌入的 NUL 会截断长度)。换句话说,输入在程序的某个较早点进行了验证,并记录了其长度,但输入未显式终止 NUL。作为假设示例,文本流上的扫描程序可能具有此属性。
正如addy2012所指出的,strncmp()
可用于前缀匹配。我专注于词典排序。但是,(1) 如果前缀字符串的长度用作长度参数,则需要将两个参数都 NUL 终止,以防止读取比前缀字符串短的输入字符串。(2) 如果前缀字符串和输入字符串之间的最小长度已知,那么memcmp()
以更低的 CPU 成本提供等效功能且不损失可读性方面将是更好的选择。
strncmp()
函数实际上只有一个用例:其中一个字符串具有已知长度,另一个字符串已知为 NUL 已终止。
不,您可以使用它来比较两个字符串的开头,无论任何字符串的长度是否已知。例如,如果您有一个带有姓氏的数组/列表,并且您想查找所有以"Mac"开头的内容。
事实上,strncmp
通常应该优先使用strcmp
,除非您绝对知道这两个字符串都是格式正确的并且nul
终止。
为什么?因为否则您有缓冲区溢出的漏洞。
不幸的是,这条规则并不经常被遵循。
存在很多缓冲区溢出错误。
更新
我认为这里的核心错误在于"其中一个字符串具有已知长度"。 没有 C 字符串具有先验的已知长度。它们不像 Pascal 或 Java 字符串,它们本质上是一对(长度、缓冲区)。根据定义,C 字符串是标识内存块的char[]
,