strncmp相比strcmp的优势是什么?

18

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

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

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

2
可能是为什么应该使用strncpy而不是strcpy?的重复问题。 - Spikatrix
8个回答

25

strcmp的问题在于,有时候如果不小心传递了无效的C字符串参数(也就是p1或p2没有以空字符结尾,即不是NULL-terminated String),那么strcmp会继续比较直到达到不可访问的内存并且崩溃,或者有时会导致意外的行为。

使用strncmp可以限制搜索范围,以避免达到不可访问的内存。

但是,不能因此得出strcmp不安全的结论。这两个函数都能够正常工作,程序员在使用之前应该阅读该函数的man页面,并在向库函数传递参数时保持诚实。

您还可以阅读THIS,其中包含一个几乎相似的问题。


我的问题是:为了确保我不会到达不可访问的内存,并仍然确保获得任何有效输入,应该设置多少值给n? - dhein
1
strcmp 函数用于比较字符串,如果您传递的缓冲区不是字符串,您期望什么语义? - chqrlie
@CoolGuy,这是百分之百正确的,它可能导致未定义行为而不是崩溃。 - Surajeet Bharati
1
@chqrlie,回答者已更新答案。他/她的意思是不是C字符串,即不是以NUL结尾的字符串。 - Spikatrix
5
“如果出错”这个想法并不令人信服。strncmp不是一种加固函数。它在语义上与strcmp不同,具有不同的用途。 - R.. GitHub STOP HELPING ICE
显示剩余5条评论

17

strncmp 没有比 strcmp 更好的 "优点"; 相反,它们解决不同的问题。 strcmp 是用来确定两个字符串是否相等(如果不相等,则可能如何根据彼此排序/排序)。strncmp 主要用于确定一个字符串是否以特定前缀开头。例如:

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

将确定str是否以"--option="开头。这不能通过strcmp来实现,因为要么修改要检查的字符串(这可能不是有效的操作),要么进行无用的复制。除非您已经知道str指向的对象至少有9个字节的长度;否则调用memcmp将具有未定义的行为。

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


10

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

就是这样。


要比较一个初始子字符串,您也可以使用 memcmp - chqrlie
3
@chqrlie 是的,但是必须注意确保子字符串不长于完整字符串。 - Some programmer dude
1
就像@JoachimPileborg所说的那样 - 在这种情况下,除非你知道要比较的字符串至少和子字符串一样长,否则memcmp是不合法的。 - R.. GitHub STOP HELPING ICE

5
在同一用户场景下,需要比较整个字符串时...
 strncmp(inputString, "LiternalString", strlen("LiternalString"));

考虑字符串inputString字符串字面值长的情况。
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的代码中并未正确计算,而且对于s1s2来说,最佳的n是不同的。

大小限制n适用于s1s2,因此使该函数在比较前缀时非常有用,但并不"更安全"。

在OP的情况下,没有理由限制搜索范围,因为由于字符串文字总是包含一个终止null字符,所以不存在"安全性"的问题。

如果有什么问题,n应该基于inputString的某些属性。

strncmp(s1, string_literal, strlen(s1))这样的代码在功能上是不正确的,因为比较会漏掉null字符。稍微好一点的是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() 的“更安全”的版本。它是用于比较字符串前缀的工具。

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;
}

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已经以null结尾,而dev2可能没有。ret1 = 0(误报结果),而不是ret2 = -1(有效结果)
结论:strncmp不仅仅是比strcmp更安全的方法。这取决于你如何使用它。
我会在字符串上使用strcmp,在子字符串搜索中使用strncmp。

-2

如果其中一个参数不是以 null 结尾的字符串,strcmp 可能会导致存储违规和分段错误。请看为什么应该使用 strncpy 而不是 strcpy。在 strcmp 中不太可能发生后果,但问题是一样的。 strnxxx 函数族试图防止读取/写入未获得的内存。

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

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


strncmpstrcmp具有不同的语义。将一个用于另一个并不是一种加固方法。strncpystrcpy在大多数程序员不知道的方式中是不同的。为了通过限制复制到目标缓冲区来防止缓冲区溢出,strlcpystrncpy好得多。如果您的系统没有它,则建议实现自己的版本。 - chqrlie
1
Strncmp只有在您知道其中一个字符串可能没有以null结尾并且您知道哪个字符串是时,才能防止溢出。如果您使用strncmp替换strcmp,那么当您不希望子字符串匹配返回true时,您可能会让自己处于开放状态。 - Robert Fisher

-3

我只能看到一个优点,就是strncmp的执行时间比strcmp稍微短一些,因为我们总是只比较字符串的前缀而不是整个字符串。

我认为strcmp和strncmp算法没有涉及任何安全方面的问题。它们的唯一区别在于strncmp只比较前n个字符。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接