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] != '\0');
}
return result;
}
但是,在将此构造内联到代码中时,当需要采取特殊操作时,会导致0
的双重测试:
int result = strncmp(key, input, inputlen);
if (result == 0) {
result = (key[inputlen] != '\0');
}
if (result == 0) {
do_something();
} else {
do_something_else();
}
内联调用的动机是因为独立函数是深奥的:重要的是哪个字符串参数是NUL终止的,哪个不是。
请注意,问题不是关于性能,而是关于编写惯用代码和采用编码风格的最佳实践。我看到比较有一些DRY违规。有没有直接的方法来避免重复?
† 已知长度 ,我的意思是长度是正确的(没有嵌入的NUL会截断长度)。换句话说,输入在程序的某个早期点验证,并记录其长度,但输入未明确NUL终止。作为一个假设的例子,文本流上的扫描仪可以具有此属性 ‡正如addy2012所指出的那样,
strncmp()
可用于前缀匹配。我专注于词典排序。但是,(1)如果前缀字符串的长度用作长度参数,则两个参数都需要终止NUL以防止读取超过前缀字符串的输入字符串。 (2)如果在前缀字符串和输入字符串之间已知最小长度,那么memcmp()
在提供等效功能方面将是更好的选择,同时降低CPU成本并且不会损失可读性。 子>
答案 0 :(得分:4)
strncmp()
函数实际上只有一个用例:其中一个字符串具有已知长度,另一个字符串已知 NUL终止了。
不,无论是否已知任何字符串的长度,您都可以使用它来比较两个字符串的开头。例如,如果你有一个数组/一个包含姓氏的列表,并且你想要找到以" Mac"开头的所有内容。
答案 1 :(得分:0)
事实上,strncmp
通常应优先使用strcmp
,除非您 完全知道两个字符串格式正确且nul
- 封端的
为什么呢?因为否则你有漏洞溢出的漏洞。
遗憾的是,此规则并未经常出现。
存在大量缓冲区溢出错误。
<强>更新强>
我认为这里的核心错误是“其中一个字符串已知长度”。没有C字符串具有已知长度先验。它们不像Pascal或Java字符串,它们本质上是一对(长度,缓冲区)。根据定义,C字符串是一个char[]
,用于标识一块内存,使用区分符号\0
来标识结尾。存在strncmp
,strncpy
等,以防止尝试将一块内存用作格式不正确的字符串。