2009-10-21 157 views
5

我想了解strcpy和strncpy的区别/缺点。 有人可以请帮助:当dest缓冲区小于src缓冲区时strcpy

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”字符串得到了修改。

请解释。

+0

这称为缓冲区Overun。 – lsalamon 2009-10-21 16:00:36

+0

此缓冲区溢出问题说明了为什么您应该选择使用strcpy而不是strcpy。 – 2009-10-21 16:03:24

+0

我无法重现您的代码。我在dest数组中获得'tring'。 – Spidey 2009-10-21 16:35:07

回答

11

简单的答案是你有(用这个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 NULL字符。

现在的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现在“包含”完整的字符串“这是一个很长的字符串”(如果你计算溢出的内存),src现在包含一个完全不同的以NUL结尾的字符串“long string”。

7

这是一个缓冲区溢出和undefined行为。

在你的情况,似乎编译器已放置destsrc顺序在内存中,当你拷贝从srcdest,继续复制过去的dest年底覆盖的src一部分。

+0

你能否说得更具描述性?让我们考虑一下我的dest和src在内存中是连续的。所以他们应该从位置1000开始(说).. 因此,dest需要1000-1004(5个字节),src需要22个字节(21个字符+空终止)。 所以我们总共使用了27个字节1000到1026. 如果我提到的(在内存方面)是正确的,请详细解释src是如何被覆盖的? – user193891 2009-10-21 16:03:39

+2

并且src和dst似乎与4个字符的倍数对齐,所以在dest长度为5个字符的情况下,新的mangled src将启动8个字符到原始src(它现在为dest)。 – ndim 2009-10-21 16:04:33

+0

您能否解释一下内存映射,以便更容易理解?只是假设所有分配的内存都从地址1000开始分配..你能解释strcpy前后dest和src占用的空间吗? – user193891 2009-10-21 16:21:17

1

高度亲切的字符串是确切的邻居。所以在你的情况下,你可能有这张图片

dst | | | | | src | | | | | |

所以你开始写,它发生了src的字段被覆盖。

Howerver你肯定不会依赖它。一切都可能发生,你有什么是未定义的行为。所以其他时间和/或其他选项可能会在其他计算机上发生其他情况。

问候 弗里德里希

0

我建议的快速阅读:

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

这表明你的差异。基本上,strncpy可以让你指定要复制的字节数,这意味着结果字符串不一定是终止的。

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

当然,这个例子中的dst只有5个字节。那么会发生什么?它不断地写作,直到目的地的结尾,然后继续记忆。在这种情况下,堆栈中的下一部分内存是您的src字符串。所以虽然你的代码并不是有意的拷贝它,但是内存中的字节布局加上dst结尾的写入操作导致了这一点。

希望有帮助!

1

您的代码导致缓冲区溢出 - 复制到dest的目标字符多于它所能容纳的字符数。 附加字符被写在栈上的另一个地方,在你的情况下,src指向的地方。

您需要使用strncpy()函数。

0

无论是我误解你的问题,或者你误解的strcpy:

问:我不明白,在 源刺痛是怎么修改。按照 解释的strcpy应该保持 复制,直到它遇到“\ 0”,所以 它,但怎么来的“SRC”字符串得到 修改。

这听起来好像是你期待strcpy在到达dest结尾时停止复制到dest,基于看到一个\0字符,这不是它所做的,strcpy将复制到目标,直到到达源字符串的末尾,由\0字符分隔。它假定你为拷贝分配了足够的内存,在拷贝之前,dest缓冲区可以包含任何内容,包括所有的空值。strncpy通过让你真正地告诉它你复制的缓冲区有多大来解决这个问题,所以你可以避免复制超过适合的情况。

1

作为附加说明,请记住,strncpy功能不适合在需要使用缓冲区溢出保护进行复制时使用。这个功能并不是为此目的而设计的,从来没有用于这个目的。 strncpy是很久以前创建的一个函数,用于在某些旧版本的UNIX中的某些非常特定的文件系统中执行一些非常特定于应用程序的字符串复制。不幸的是,该图书馆的作者设法“剽窃”了通用冠冕堂皇的名字strncpy,以用于这个非常狭窄和特定的目的。为了向后兼容的目的,它被保存下来。而现在,我们有一两代程序员完全根据其名称做出关于strncpy目的的假设,因此不恰当地使用它。实际上,strncpy几乎没有任何有意义的用途。

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

P.S. StackOverflow上的这个thread包含了一个关于真正目的strncpy开发的好的讨论。请参阅此post专门用于精确说明其在UNIX文件系统中的角色。此外,请参阅here以获得有关如何获得strncpy的好文章。

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

+0

我发现strncpy有用说,如果我有一个缓冲区,并从文本文件读取行,我知道这将是 2009-10-21 17:45:52

+0

@詹姆斯莫里斯:我没有看到你到底想通过在那里使用'strncpy'来达到什么目的。在这种情况下,'strncpy'的哪个特性对你很重要? – AnT 2009-10-21 17:53:53

+0

这种方式的答案不正确。 strncpy肯定不会超过一个缓冲区,如果你有一个“字符串” b)你有正确的长度适当的空间。 对于这一切,你可能最好使用下面的代码(伪C,部分取自代码完成II) char buf [MAX_LEN + 1]; strncpy(buf,src,MAX_LEN); 在这种情况下,strncpy比strcpy更安全。但是你说的是,标准C中没有一个功能可以“缓冲”超限保护。这就是C如何实现,速度,速度,速度 – Friedrich 2009-10-22 03:40:59