【问题标题】:strcpy when dest buffer is smaller than src buffer当 dest 缓冲区小于 src 缓冲区时的 strcpy
【发布时间】:2010-12-08 18:39:02
【问题描述】:

我试图了解 strcpy 和 strncpy 的区别/缺点。 有人可以帮忙吗:

void main()
{
char src[] = "this is a long string";
char dest[5];

strcpy(dest,src) ;
printf("%s \n", dest);
printf("%s \n", src);

}

输出是:

this is a long string 
a long string 

问题:我不明白,源代码是如何被修改的。根据解释,strcpy 应该一直复制,直到遇到 '\0',所以确实如此,但是“src”字符串是如何被修改的。

请解释一下。

【问题讨论】:

  • 这称为缓冲区溢出。
  • 这个缓冲区溢出问题说明了为什么你应该选择使用 strncopy 而不是 strcpy。
  • 我无法重现您的代码。我在 dest 数组中得到 tring
  • @Spidey,这是未定义的行为。 c-faq.com/ansi/undef.html "undefined: 任何事情都可能发生;标准没有强加任何要求。程序可能无法编译,或者执行不正确(崩溃或静默生成不正确的结果),或者它可能幸运地完全按照程序员的意图去做。”
  • 谢谢大家。特别是因为您不仅解释了答案,而且还以图形方式显示了它在内存中的工作方式(对于我的案例中显示的行为)。还注意到这可能会引发不同的结果,因为它是未定义的行为。

标签: c string buffer-overflow strcpy


【解决方案1】:

这是缓冲区溢出和未定义的行为。

在您的情况下,编译器似乎已将destsrc 按顺序放置在内存中。当您从src 复制到dest 时,它会继续复制到dest 的末尾并覆盖src 的一部分。

【讨论】:

  • 您能否描述得更详细一些。让我们考虑一下我的 dest 和 src 在内存中是连续的。所以他们应该从位置 1000(比如说)开始。所以 dest 需要 1000-1004(5 个字节), src 需要 22 个字节(21 个字符 + 空终止)。所以我们总共使用了 27 个字节 1000 到 1026。如果我提到的(在内存方面)是正确的,你能详细解释一下 src 是如何被覆盖的吗?
  • 并且 src 和 dst 似乎与 4 个字符的倍数对齐,因此 dest 的长度为 5 个字符,新的损坏的 src 将从原始 src 的 8 个字符开始(现在是 dest)。
  • 你能解释一下内存映射,让它更容易理解吗?假设所有分配的内存都从位置 1000 开始。你能解释一下 strcpy 前后 dest 和 src 占用的空间吗?
  • dest和src占用的空间不变。为它们分配的空间量是恒定的。改变的是内存的实际内容。因为您在副本中导致缓冲区溢出,所以您不仅更改了为 dest 分配的内存区域,还更改了为 src 分配的一些内存。
  • 我在下面的答案中添加了图形解释。
【解决方案2】:

字符串很可能是精确的邻居。所以在你的情况下,你可能有这张照片

dst | | | | |源代码 | | | | | |

所以你开始写,碰巧src的字段被覆盖了。

但是,您当然不能依赖它。一切都可能发生你所拥有的是未定义的行为。因此,其他时间和/或其他选项可能会在另一台计算机上发生其他事情。

问候 弗里德里希

【讨论】:

    【解决方案3】:

    我建议快速阅读:

    http://en.wikipedia.org/wiki/Strncpy#strncpy

    它向您展示了差异。本质上,strncpy 允许您指定要复制的字节数,这意味着结果字符串不一定以 null 结尾。

    现在,当您使用 strcpy 将一个字符串复制到另一个字符串时,它不会检查生成的内存区域以查看它是否足够大 - 在这方面它不会握住您的手。它最多检查 src 字符串中的空字符。

    当然,这个例子中的 dst 只有 5 个字节。那么会发生什么?它一直在写,直到 dest 的末尾,然后在内存中过去。在这种情况下,堆栈上的下一部分内存是您的 src 字符串。因此,虽然您的代码不是故意复制它,但内存中字节的布局加上 dst 结束后的写入导致了这种情况。

    希望有帮助!

    【讨论】:

      【解决方案4】:

      简单的答案是,您(通过 strcpy() 调用)做了超出系统规范的事情,因此遭受未定义行为的影响是理所当然的。

      更困难的答案涉及检查系统上的具体内存布局,以及 strcpy() 在内部如何工作。大概是这样的:

           N+28 "g0PP"
           N+24 "trin"
           N+20 "ng s"
           N+16 "a lo"
           N+12 " is "
      src  N+08 "this"
           N+04 "DPPP"
      dest N+00 "DDDD"
      

      字母D 代表dest 中的字节,字母P 是填充字节,0 字符是用作字符串终止符的ASCII NUL 字符。

      现在 strcpy(dest,src) 将稍微改变内存内容(假设它正确处理重叠的内存区域):

           N+28 "g0PP"
           N+24 "trin"
           N+20 "g0 s"
           N+16 "trin"
           N+12 "ng s"
      src  N+08 "a lo"
           N+04 " is "
      dest N+00 "this"
      

      即虽然 dest 现在“包含”完整字符串“this is a long string”(如果计算溢出的内存),src 现在包含完全不同的以 NUL 结尾的字符串“a long string”。

      【讨论】:

        【解决方案5】:

        您的代码导致缓冲区溢出 - 复制到 dest 的字符数超过了它可以容纳的字符数。 额外的字符写在堆栈上的另一个地方,在你的例子中,src 指向的地方。

        你需要使用strncpy()函数。

        【讨论】:

          【解决方案6】:

          要么我误解了你的问题,要么你误解了 strcpy:

          问题:我不明白,如何 源刺被修改。按照 解释,strcpy 应该保留 复制直到遇到'\0',所以 确实如此,但是“src”字符串是怎么来的 修改。

          在我看来,基于看到 \0 字符,您希望 strcpy 在到达 dest 末尾时停止复制到 dest。这不是它的作用。 strcpy 复制到目标,直到它到达源字符串的末尾,由\0 字符分隔。它假定您为副本分配了足够的内存。在复制之前,dest 缓冲区可以包含任何内容,包括所有空值。

          strncpy 通过让您实际告诉它您要复制到的缓冲区有多大来解决这个问题,这样您就可以避免复制超出容量的情况。

          【讨论】:

            【解决方案7】:

            作为附加说明,请记住strncpy 函数不是在您需要执行带有缓冲区溢出保护的复制时使用的正确函数。此功能不用于该目的,也从未用于该目的。 strncpy 是很久以前创建的函数,用于在某些旧版本的 UNIX 中的某些非常特定的文件系统中执行一些非常特定于应用程序的字符串复制。不幸的是,该库的作者设法“劫持”了听起来通用的名称strncpy,以用于这个非常狭窄和特定的目的。然后为了向后兼容的目的而保留它。而现在,我们有一两代程序员仅仅根据它的名字就对strncpy 的用途做出假设,从而不恰当地使用它。实际上,strncpy 几乎没有或根本没有有意义的用途。

            C 标准库(至少是其 C89/90 版本)不提供带有缓冲区溢出保护的字符串复制功能。为了执行这种受保护的复制,您必须使用一些特定于平台的函数,例如strlcpystrcpy_s,或者自己编写一个。

            P.S. StackOverflow 上的 thread 包含一个很好的讨论,讨论了 strncpy 的真正目的。请参阅此post,以详细了解其在 UNIX 文件系统中的作用。另外,请参阅 here 了解有关 strncpy 是如何形成的好文章。

            再一次,strncpy 是一个用于复制完全不同类型的字符串 - 固定长度字符串的函数。它甚至不打算与传统的 C 风格以空字符结尾的字符串一起使用。

            【讨论】:

            • 我发现 strncpy 很有用,如果我有一个缓冲区并且正在从一个我知道将是
            • @James Morris:我看不出你在那儿使用strncpy 到底想达到什么目的。在这种情况下,strncpy 的哪个特性对您来说很重要?
            • 这样的答案是不正确的。如果您 a) 为“字符串”提供适当的空间 b) 您的长度正确,则 Strncpy 肯定不会超出缓冲区。为此,您可能最好使用以下代码(伪 C,部分取自 Code Complete II) char buf[MAX_LEN+1]; strncpy(buf, src, MAX_LEN);在这种情况下,strncpy 比 strcpy 更安全。但是你是对的,标准 C 中没有一个函数可以“缓冲”溢出保护。这就是 C 的实现方式,速度,速度,速度
            • @Friedrich:答案是正确的。 strncpy 在字符串比缓冲区短时做了很多无用的工作(用零填充尾部),并且在字符串比缓冲区长时不做必须做的事情(不添加终止零)。这些是strncpy 最初是为了完全不同的目的而开发的一个简单事实的结果。诚然,也可以跳过某些环节让strncpy 在这里“工作”,但它仍然不是适合这项工作的工具。 (您在“伪代码”中也忘记初始化终止零。)
            • 我添加了一个 P.S.我的回复包含一些有用的链接,可以帮助人们理解strncpy 函数的真正用途。
            猜你喜欢
            • 2015-07-07
            • 1970-01-01
            • 1970-01-01
            • 2020-09-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多