【问题标题】:What's wrong with strcmp?strcmp 有什么问题?
【发布时间】:2014-08-12 18:14:38
【问题描述】:

在对问题Reading In A String and comparing it C的回复中,不止一个人不鼓励使用strcmp(),比如

我也强烈建议你习惯使用 strncmp() 现在,...以避免许多问题。

或(在Why does my string comparison fail?

确保您使用的是 strncmp 而不是 strcmp。 strcmp 是深刻的 不安全。

他们暗指什么问题?

之所以scanf() with string specifiersgets() are strongly discouraged 是因为它们几乎不可避免地会导致缓冲区溢出漏洞。但是,strcmp() 不可能溢出缓冲区,对吧?

“缓冲区溢出或缓冲区溢出是程序在向缓冲区写入数据时超出缓冲区边界并覆盖相邻内存的异常情况。”

( -- Wikipedia: buffer overflow).

由于 strcmp() 函数从不写入任何缓冲区,所以 strcmp() 函数不会导致缓冲区溢出,对吧?

人们不鼓励使用strcmp(),而是推荐strncmp() 的原因是什么?

【问题讨论】:

  • 你为什么不问问那些人?
  • 相信我strncmp() 并不比 strcmp() 好(或差)。
  • 链接的SO文章中的cmets大多是垃圾
  • 使用strncmp 来防止非空终止的字符串只是解决了根本问题,即您有一个未终止的字符串。它只会弄乱下一个假设它是空终止的函数。

标签: c string deprecated strcmp


【解决方案1】:

虽然strncmp 可以防止您超出缓冲区,但其主要目的不是安全。相反,它适用于只想比较(正确可能以 NUL 终止的)字符串的前 N ​​个字符的情况。

来自man page

strcmp() 函数比较两个字符串 s1s2。如果发现s1 分别小于、匹配或大于s2,则返回一个小于、等于或大于零的整数。

strncmp() 函数类似,除了它比较s1s2 的唯一第一个(最多)n 字节。

请注意,在这种情况下,strncmp 不能用简单的memcmp 替换,因为您仍然需要利用它的 stop-on-NUL 行为,以防其中一个字符串短于 n

如果strcmp 导致缓冲区溢出,则以下两种情况之一为真:

  1. 您的数据不应以 NUL 结尾,您应该改用 memcmp
  2. 您的数据应该以 NUL 结尾,但在填充缓冲区时您已经搞砸了,不知何故没有以 NUL 结尾。

请注意,读取超过缓冲区末尾仍被视为缓冲区溢出。虽然它可能看起来无害,但它可能与到最后一样危险。

读、写、执行……没关系。任何对非预期地址的内存引用都是未定义的行为。在最明显的情况下,您尝试访问未映射到进程地址空间的页面,从而导致页面错误和随后的 SIGSEGV。在最坏的情况下,您有时会遇到 \0 字节,但有时您会遇到其他缓冲区,导致程序行为不稳定。

【讨论】:

  • 我看不出 strcmp() 是如何导致缓冲区溢出的,即使这两件事都是真的。你能多说几句究竟出了什么问题吗?
  • 想象你有char buf[100],其中每个字符都是'a'(它不是以NUL 结尾的)。如果将此缓冲区传递给strcmp(假设另一个参数是更长的字符串,则strcmp 将继续在buf[100] 进行比较,依此类推,超出缓冲区。
  • 我可以看到 写入 超出缓冲区末尾是如何导致问题的。但是 strcmp() 不会那样做,对吧?如果 strcmp() 继续 reading 超过缓冲区的末尾,您介意在答案中添加几句话吗?
  • 读、写、执行……没关系。任何对非预期地址的内存引用都是未定义的行为。在最明显的情况下,您尝试访问未映射到进程地址空间的页面,从而导致页面错误和随后的 SIGSEGV。在最坏的情况下,您有时会遇到\0 字节,但有时您会遇到其他缓冲区,导致程序行为不稳定。
  • “它存在于人们只想比较(正确以 NUL 终止的)字符串的前 N ​​个字符的情况。”是正确的。从 C 规范“strncmp 函数返回一个整数......因此作为 s1 指向的可能以 null 结尾的数组”。 s1s2int strncmp(const char *s1, const char *s2, size_t n); 都不需要是 C 字符串。独立地,它们可以只是字符串,也可以是 char 的数组,而不会“正确地以 NUL 终止”。
【解决方案2】:

根据定义,字符串是“由第一个空字符终止并包括第一个空字符的连续字符序列”。

strncmp()strcmp() 更安全的唯一情况是,当您将两个字符数组作为字符串进行比较时,您确定两个数组至少有 n 字节长(第三个参数传递给strncmp()),并且您确定两个数组都包含字符串(即,包含 '\0' 空字符终止符)。

在大多数情况下,您的代码(如果正确)将保证任何应该包含以空字符结尾的字符串的数组实际上确实包含以空字符结尾的字符串。

strncmp() 中添加n 并不是让不安全代码变得安全的魔杖。它不能防止空指针、未初始化的指针、未初始化的数组、不正确的n 值或只是传递不正确的数据。您可以使用任一功能射击自己的脚。

如果您尝试调用 strcmpstrncmp 并使用您认为包含以 null 结尾的字符串但实际上没有的数组,那么您的代码已经存在错误.使用strncmp() 可能会帮助您避免该错误的直接症状,但它不会修复它。

【讨论】:

    【解决方案3】:

    strcmp 将两个字符串逐个字符进行比较,直到检测到差异或在其中一个处找到 \0

    另一方面,strncmp 提供了一种限制要比较的字符数的方法,因此如果字符串不以 \0 结尾,则在达到大小限制后,该函数将不会继续检查。

    想象一下,如果您在这两个内存区域比较两个字符串会发生什么:

    0x40, 0x41, 0x42,... 0x40, 0x41, 0x42,...

    而且您只对前两个字符感兴趣。不知何故,\0 已从字符串末尾删除,第三个字节恰好在两个区域重合。如果num 参数为2,strncmp 将避免比较第三个字节。

    编辑 正如下面的 cmets 所指出的,这种情况是由于对语言的错误或非常具体的使用造成的。

    【讨论】:

    • 如果要比较内存区域,请使用memcmp。在 C 中,“字符串”是一个以空字符结尾的字符序列。如果您有字符串,请使用strcmp。如果你不这样做,不要。
    • 当然,strcmp 会使比较“安全”,但这实际上只是推迟了问题。您的非空终止字符串随后会在您的程序中导致未定义的行为。
    • 据我了解,strncmp 不存在“安全”,而是“我想比较这些字符串的前 N ​​个字符”。
    • @JonathonReinhart:你应该这样回答。
    • 不以\0 结尾的东西不是字符串
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-12
    • 2018-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-12
    相关资源
    最近更新 更多