17

似乎strncmp通常推荐比strcmp,有什么优势?我认为这可能与安全有关。如果是这种情况,如果已知输入字符串之一是文字常量,它是否仍然适用,例如"LiteralString"

更新: 我的意思是在需要比较整个字符串的相同用户场景下,strncmp可以如下使用。我想知道这是否有意义。

strncmp(inputString, "LiternalString", strlen("LiternalString"));
4

8 回答 8

22

问题strcmp在于,有时,如果错误地传递的参数不是有效的 C 字符串(意味着 p1 或 p2 没有以空字符终止,即不是以NULL 结尾的 String),那么 strcmp 会继续比较,直到达到不可访问的内存和崩溃或有时会导致意外行为。

使用strncmp您可以限制搜索,使其不会到达不可访问的内存。

但是,据此,不应断定strcmp使用不安全。这两个功能都以它们预期的方式运行良好。程序员man在使用该函数之前应阅读该函数的页面,并且在将参数传递给此类库函数时必须足够真诚。

您还可以阅读包含几乎类似问题的THIS 。

于 2015-05-12T12:17:24.777 回答
15

strncmp没有“优势strcmp”;而是他们解决不同的问题。strcmp用于确定两个字符串是否相等(如果不相等,可能如何对它们进行排序/排序)。strncmp(主要)用于确定字符串是否以特定前缀开头。例如:

if (strncmp(str, "--option=", 9)==0)

将确定是否str"--option=". 如果不strcmp修改要检查的字符串(这可能不是有效操作)或制作无用的副本,这是无法实现的。memcmp除非您已经知道str指向长度至少为 9 个字节的对象,否则它也无法实现;否则调用memcmp将具有未定义的行为。

还有其他用例strncmp,例如使用非 C 字符串数据。

于 2015-05-12T13:16:36.353 回答
10

这完全取决于您的用例。如果strncmp您只需要比较固定数量的字符,请使用,strcmp如果您需要比较整个字符串,请使用。

就是这样。

于 2015-05-12T12:12:27.790 回答
3

...在需要比较整个字符串的相同用户场景下,...

 strncmp(inputString, "LiternalString", strlen("LiternalString"));

考虑 stringinputStringstring literal长的情况。

const char *inputString = "LiternalString_XYZ";
int cmp = strncmp(inputString, "LiternalString", strlen("LiternalString"));
printf("%d\n", cmp); // prints 0.

但是 OP 想要比较整个字符串

const char *inputString = "LiternalString_XYZ";
int cmp = strcmp(inputString, "LiternalString");
printf("%d\n", cmp); // prints non-zero.

OP直接问题的结论

我想知道这是否有意义。

不。要比较整个字符串strncmp()无法始终提供正确的结果。使用strcmp().


更远

strncmp(s1,s2,n)可以限制搜索,以便在正确计算的情况 下它不会到达不可访问的内存n——这在 OP 的代码中没有完成,并且当最好ns1s2.

大小限制n适用于两者s1s2因此使此函数可用于比较前缀,但不是“更安全”。

在 OP 的情况下,由于字符串文字的字符串长度,没有理由因为“安全”而限制搜索,因为字符串文字 总是包含终止的null 字符

如果有的话,n应该是基于inputString.

类似的代码strncmp(s1, string_literal, strlen(s1))在功能上不正确,因为比较错过了空字符。稍微好一点的是strncmp(s1, string_literal, strlen(s1)+1),但这在功能上与更简单的相同,strcmp(s1, string_literal)而不会降低“安全性”。


以下提供了一些改进的“安全性”,以防foo()没有正确形成 字符串N != M,但如果如上所述,则可以提供错误的答案。

char s1[N];
foo(s1); // populate s1 somehow
int cmp = strncmp(s1, string_literal, sizeof s1);

char s1[N];
char s2[M];
foo(s1); // populate s1 somehow
foo(s2); // populate s2 somehow
int cmp = strncmp(s1, s2, min(sizeof s1, sizeof s2));

然而在这些情况下,问题出在foo(),而不是在这里。
对我来说,如果foo()有问题,我会使用以下

s1[sizeof s1 - 1] = '\0';
s2[sizeof s2 - 1] = '\0';
int cmp = strcmp(s1, s2);

或检测到foo()没有形成字符串

char s1[N];
foo(s1);
if (memchr(s1, '\0', sizeof s1) == NULL) Oops();

故事的寓意:strncmp()不是strcmp(). 它是用于不同工作的工具:比较字符串前缀。

于 2019-11-09T17:17:36.610 回答
1

只需在此处发布一个反 strncmp 用例。想想下面的代码。

#include <stdio.h>
#include <dirent.h>

int main()
{
    //I want to list all files under current folder, include hidden files.
    DIR *dir;
    struct dirent *dp;
    char * file_name;
    dir = opendir(".");
    while ((dp=readdir(dir)) != NULL) {
        printf("debug: %s\n", dp->d_name);
        if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") )
        //It doesn't work if you replace strcmp with strncmp here.
        //if ( !strncmp(dp->d_name, ".", 1) || !strncmp(dp->d_name, "..", 2) )  
        {
        } else {
            file_name = dp->d_name;
            printf("file_name: \"%s\"\n",file_name);
        }
    }
    closedir(dir);
    return 0;
}
于 2016-09-16T22:58:34.117 回答
1
int ret1, ret2;
char dev1[]  = { "ABC" };
char dev2[4] = { "ABC" };

dev2[3] = 'D';
ret1 = strncmp(dev1,dev2,strlen(dev1));  // # of characters
ret2 = strncmp(dev1,dev2,sizeof(dev1));  // # of characters plus '\0'

假设: dev1 是空终止的,而 dev2 可能不是。ret1 = 0(假阳性结果),而不是 ret2=-1(有效结果)

fazit:strncmp 不仅仅是 strcmp 的一种更安全的方式。取决于你如何使用它。

我会在字符串上使用 strcmp,在子字符串搜索上使用 strncmp。

于 2018-03-07T06:36:47.520 回答
-1

如果参数之一不是以空字符结尾的字符串,strcmp 可能会导致存储波动和分段错误。看看为什么你应该使用 strncpy 而不是 strcpy。strcmp 中不太可能发生后果,但问题是相同的。strnxxx 函数族尝试防止读取/写入未获取的内存。

使用 strn 的缺点是对计数器进行额外的比较和减量操作。

简而言之:strncmp 比 strcmp 更安全,但也更慢。

于 2015-05-12T12:31:09.657 回答
-2

我只能看到一个优势,strncmp 的执行时间比 strcmp 的执行时间略短,因为我们总是将字符串的前缀与 comapre 进行比较,而不是整个字符串。

我不认为 strcmp 和 strncmp 算法涉及任何安全方面。它们是相同的,只是在 strncmp 中只比较前 'n' 个字符。

于 2015-05-12T12:47:18.527 回答