是否习惯使用strncmp()?

时间:2015-05-05 23:23:15

标签: c strncmp

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成本并且不会损失可读性。   

2 个答案:

答案 0 :(得分:4)

  

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

     

其中一个字符串具有已知长度,另一个字符串已知   NUL终止了。

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

答案 1 :(得分:0)

事实上,strncmp通常应优先使用strcmp,除非您 完全知道两个字符串格式正确且nul - 封端的

为什么呢?因为否则你有漏洞溢出的漏洞。

遗憾的是,此规则并未经常出现。

存在大量缓冲区溢出错误。

<强>更新

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