似乎通常推荐使用 strncmp
而不是 strcmp
,这有什么优点?我认为这可能与安全有关。如果是这种情况,那么如果其中一个输入字符串已知为文字常量,例如 "LiteralString"
,它是否仍然适用?
更新:
我的意思是在需要比较整个字符串的相同用户场景下,可以使用如下的 strncmp
。我想知道这是否有意义。
strncmp(inputString, "LiternalString", strlen("LiternalString"));
似乎通常推荐使用 strncmp
而不是 strcmp
,这有什么优点?我认为这可能与安全有关。如果是这种情况,那么如果其中一个输入字符串已知为文字常量,例如 "LiteralString"
,它是否仍然适用?
更新:
我的意思是在需要比较整个字符串的相同用户场景下,可以使用如下的 strncmp
。我想知道这是否有意义。
strncmp(inputString, "LiternalString", strlen("LiternalString"));
strcmp
的问题在于,有时候如果不小心传递了无效的C字符串参数(也就是p1或p2没有以空字符结尾,即不是NULL-terminated String),那么strcmp会继续比较直到达到不可访问的内存并且崩溃,或者有时会导致意外的行为。
使用strncmp
可以限制搜索范围,以避免达到不可访问的内存。
但是,不能因此得出strcmp
不安全的结论。这两个函数都能够正常工作,程序员在使用之前应该阅读该函数的man
页面,并在向库函数传递参数时保持诚实。
您还可以阅读THIS,其中包含一个几乎相似的问题。
strcmp
函数用于比较字符串,如果您传递的缓冲区不是字符串,您期望什么语义? - chqrliestrncmp
不是一种加固函数。它在语义上与strcmp
不同,具有不同的用途。 - R.. GitHub STOP HELPING ICEstrncmp
没有比 strcmp
更好的 "优点"; 相反,它们解决不同的问题。 strcmp
是用来确定两个字符串是否相等(如果不相等,则可能如何根据彼此排序/排序)。strncmp
主要用于确定一个字符串是否以特定前缀开头。例如:
if (strncmp(str, "--option=", 9)==0)
将确定str
是否以"--option="
开头。这不能通过strcmp
来实现,因为要么修改要检查的字符串(这可能不是有效的操作),要么进行无用的复制。除非您已经知道str
指向的对象至少有9个字节的长度;否则调用memcmp
将具有未定义的行为。
strncmp
还有其他用途,例如处理非C字符串数据。
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的代码中并未正确计算,而且对于s1
和s2
来说,最佳的n
是不同的。
大小限制n
适用于s1
和s2
,因此使该函数在比较前缀时非常有用,但并不"更安全"。
在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()
的“更安全”的版本。它是用于比较字符串前缀的工具。请在此处发布一个反对使用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;
}
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'
如果其中一个参数不是以 null 结尾的字符串,strcmp 可能会导致存储违规和分段错误。请看为什么应该使用 strncpy 而不是 strcpy。在 strcmp 中不太可能发生后果,但问题是一样的。 strnxxx 函数族试图防止读取/写入未获得的内存。
使用 strn 的缺点是在计数器上进行额外的比较和减量操作。
简而言之:strncmp 比 strcmp 更安全,但速度也更慢。
strncmp
和strcmp
具有不同的语义。将一个用于另一个并不是一种加固方法。strncpy
与strcpy
在大多数程序员不知道的方式中是不同的。为了通过限制复制到目标缓冲区来防止缓冲区溢出,strlcpy
比strncpy好得多。如果您的系统没有它,则建议实现自己的版本。 - chqrlie我只能看到一个优点,就是strncmp的执行时间比strcmp稍微短一些,因为我们总是只比较字符串的前缀而不是整个字符串。
我认为strcmp和strncmp算法没有涉及任何安全方面的问题。它们的唯一区别在于strncmp只比较前n个字符。