【问题标题】:Advantages of strncmp over strcmp?strncmp 相对于 strcmp 的优势?
【发布时间】:2015-07-23 07:15:54
【问题描述】:

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

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

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

【问题讨论】:

标签: c c-standard-library


【解决方案1】:

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

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

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

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

【讨论】:

  • 我现在的问题是:为 n 设置什么值,以确保我不会到达不可访问的内存,并且仍然确保获得任何有效的输入。
  • strcmp 函数是比较字符串,它传递的缓冲区不是字符串,你期望什么语义?
  • @Zaibis,要为n 设置值,其中一个参数必须是一个有效字符串,其length 可以分配给n,就像您在问题的更新部分中所做的那样.
  • @chqrlie ,回答者已更新答案。他/她的意思是说不是 C 字符串,即不是 NUL 终止的字符串
  • 整个“如果错误”的想法并不令人信服。 strncmp 不是强化功能。它在语义上与strcmp 不同,用途也不同。
【解决方案2】:

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

 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 的代码中没有完成,并且在最佳ns1s2 不同。

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

在 OP 的情况下,由于 string literal 作为 string literals 的字符串长度,没有理由因为“安全”而限制搜索总是 包含一个终止空字符

如果有的话,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() 的“更安全”版本。它是用于不同工作的工具:比较字符串前缀。

【讨论】:

    【解决方案3】:
    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。

    【讨论】:

      【解决方案4】:

      只需在此处发布一个反 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;
      }
      

      【讨论】:

        【解决方案5】:

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

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

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

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

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

            如果参数之一不是以空值结尾的字符串,则 strcmp 可能会导致存储波动和分段错误。看看why you should use strncpy instead of strcpy。 strcmp 中不太可能发生后果,但问题是相同的。 strnxxx 函数族尽量防止读取/写入未获取的内存。

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

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

            【讨论】:

            • strncmpstrcmp 具有不同的语义。将一个用于另一个不是硬化方法。 strncpystrcpy 的不同之处在于大多数程序员都不知道。为了通过限制复制到目标缓冲区来防止缓冲区溢出,strlcpystrncpy很多。如果您的系统没有它,建议您实现自己的版本。
            • Strncmp 仅在您知道只有一个字符串可能不是以空值结尾的并且您知道它是两个字符串中的哪一个时才能防止溢出。如果您使用 strncmp 作为 strcmp 的替代品,您可能会敞开心扉,让子字符串匹配在您不希望的情况下恢复为真。
            【解决方案8】:

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

            就是这样。

            【讨论】:

            • 比较一个初始子串,也可以使用memcmp
            • @chqrlie 是的,但必须注意确保子字符串不长于完整字符串。
            • @JoachimPileborg 所说的 - memcmp 在这种情况下是不合法的,除非您知道要比较的字符串至少与子字符串一样长。
            猜你喜欢
            • 2017-01-02
            • 2017-03-19
            • 2010-12-23
            • 2011-02-26
            • 1970-01-01
            • 2012-05-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多