0

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 违规。有没有一种简单的方法来避免重复?


† 通过known length,我的意思是长度是正确的(没有嵌入的 NUL 会截断长度)。换句话说,输入在程序中的某个较早时间点进行了验证,并记录了它的长度,但输入没有显式地以 NUL 终止。作为一个假设的例子,文本流上的扫描仪可能具有此属性。‡ 正如addy2012
所指出的,可用于前缀匹配。我专注于字典顺序。但是,(1)如果前缀字符串的长度用作长度参数,则两个参数都需要以 NUL 结尾,以防止读取比前缀字符串短的输入字符串。(2) 如果已知前缀字符串和输入字符串之间的最小长度,则strncmp()memcmp()在以更少的 CPU 成本提供等效功能并且不损失可读性方面,这将是一个更好的选择。

4

2 回答 2

4

strncmp()函数实际上只有一个用例:

其中一个字符串的长度已知,另一个字符串已知以 NUL 结尾。

不,您可以使用它来比较两个字符串的开头,无论任何字符串的长度是否已知。例如,如果您有一个带有姓氏的数组/列表,并且您想查找所有以“Mac”开头的内容。

于 2015-05-05T23:30:24.133 回答
0

实际上,strncmp通常应该优先使用,strcmp除非您绝对知道 两个字符串都是格式正确且 -终止的。nul

为什么?因为否则你就有缓冲区溢出的漏洞。

不幸的是,这条规则并不经常被遵守。

有很多缓冲区溢出错误。

更新

我认为这里的核心错误在于“其中一个字符串的长度已知”。没有 C 字符串具有先验已知长度。它们不像 Pascal 或 Java 字符串,它们本质上是一对(长度,缓冲区)。AC字符串根据定义是一个char[]标识一块内存,用区分符号\0来标识结束。 strncmpstrncpy存在以防止尝试将一块内存用作格式不正确的字符串。

于 2015-05-05T23:33:37.170 回答