为什么这种故意错误地使用 strcpy 不会严重失败?
为什么下面使用 strcpy
的 C 代码对我来说效果很好?我尝试通过两种方式使其失败:
1)我尝试将 strcpy
从字符串文字转移到分配的内存中,但分配的内存太小而无法容纳它。它复制了整个事情并且没有抱怨。
2) 我尝试从一个非 NUL
终止的数组中调用 strcpy
。 strcpy
和 printf
工作得很好。我原以为 strcpy
会复制 char
直到找到 NUL
,但什么也没有出现,它仍然停止了。
为什么这些都不会失败?我只是在某种程度上“幸运”,还是我误解了这个功能的工作原理?它是特定于我的平台(OS X Lion)的,还是大多数现代平台都以这种方式工作?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *src1 = "123456789";
char *dst1 = (char *)malloc( 5 );
char src2[5] = {'h','e','l','l','o'};
char *dst2 = (char *)malloc( 6 );
printf("src1: %s\n", src1);
strcpy(dst1, src1);
printf("dst1: %s\n", dst1);
strcpy(dst2, src2);
printf("src2: %s\n", src2);
dst2[5] = '\0';
printf("dst2: %s\n", dst2);
return 0;
}
运行此代码的输出是:
$ ./a.out
src1: 123456789
dst1: 123456789
src2: hello
dst2: hello
Why does the below C code using strcpy
work just fine for me? I tried to make it fail in two ways:
1) I tried strcpy
from a string literal into allocated memory that was too small to contain it. It copied the whole thing and didn't complain.
2) I tried strcpy
from an array that was not NUL
-terminated. The strcpy
and the printf
worked just fine. I had thought that strcpy
copied char
s until a NUL
was found, but none was present and it still stopped.
Why don't these fail? Am I just getting "lucky" in some way, or am I misunderstanding how this function works? Is it specific to my platform (OS X Lion), or do most modern platforms work this way?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *src1 = "123456789";
char *dst1 = (char *)malloc( 5 );
char src2[5] = {'h','e','l','l','o'};
char *dst2 = (char *)malloc( 6 );
printf("src1: %s\n", src1);
strcpy(dst1, src1);
printf("dst1: %s\n", dst1);
strcpy(dst2, src2);
printf("src2: %s\n", src2);
dst2[5] = '\0';
printf("dst2: %s\n", dst2);
return 0;
}
The output from running this code is:
$ ./a.out
src1: 123456789
dst1: 123456789
src2: hello
dst2: hello
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
首先,复制到太小的数组中:
C 对于超出数组边界没有保护,因此如果
dst1[5..9]
处没有任何敏感内容,然后你很幸运,副本进入了你不合法拥有的内存,但它也不会崩溃。但是,该内存并不安全,因为它尚未分配给您的变量。另一个变量很可能会分配给它的内存,然后覆盖您放入其中的数据,从而稍后损坏您的字符串。其次,从非空终止的数组中进行复制:
尽管我们通常被告知内存中充满了任意数据,但其中很大一部分会被清零。即使您没有在
src2
中放置空终止符,src[5]
很可能恰好是\ 0 无论如何。这样就可以复制成功了。请注意,这是不保证的,并且可能在任何时间、任何平台上的任何运行中失败。但这次你很幸运(可能是大多数时候),而且它奏效了。
First, copying into an array that is too small:
C has no protection for going past array bounds, so if there is nothing sensitive at
dst1[5..9]
, then you get lucky, and the copy goes into memory that you don't rightfully own, but it doesn't crash either. However, that memory is not safe, because it has not been allocated to your variable. Another variable may well have that memory allocated to it, and later overwrite the data you put in there, corrupting your string later on.Secondly, copying from an array that is not null-terminated:
Even though we're usually taught that memory is full of arbitrary data, huge chunks of it are zero'd out. Even though you didn't put a null-terminator in
src2
, chances are good thatsrc[5]
happens to be\0
anyway. This makes the copy succeed. Note that this is NOT guaranteed, and could fail on any run, on any platform, at anytime. But you got lucky this time (and probably most of the time), and it worked.超出分配内存范围的覆盖会导致未定义的行为。
所以在某种程度上,是的,你很幸运。
未定义的行为意味着任何事情都可能发生,并且该行为无法解释,因为定义语言规则的标准没有定义任何行为。
编辑:
再想一想,我想说你真的不幸,程序运行良好并且没有崩溃。现在有效并不意味着它永远有效,事实上,它是一颗滴答作响的炸弹。
根据墨菲定律:
“任何可能出错的事情都会出错”
[
“而且很可能是在最不方便的时刻”< code>][
]
- 是我对法律的编辑:)Overwriting beyond the bounds of allocated memory causes Undefined Behavior.
So in a way yes you got lucky.
Undefined behavior means anything can happen and the behavior cannot be explained as the Standard, which defines the rules of the language, does not define any behavior.
EDIT:
On Second thoughts, I would say you are really Unlucky here that the program works fine and does not crash. It works now does not mean it will work always, In fact it is a bomb ticking to blow off.
As per Murphy's Law:
"Anything that can go wrong will go wrong"
[
"and most likely at the most inconvenient possible moment"]
[
]
- Is my edit to the Law :)是的,你只是很幸运。
通常,堆是连续的。这意味着当您写入经过
malloc
的内存时,可能会损坏后续内存块或用户内存块之间可能存在的某些内部数据结构。这种损坏通常在有问题的代码出现很久之后才显现出来,这使得调试此类错误变得困难。您可能会收到
NUL
,因为内存恰好是零填充的(无法保证)。Yes, you're quite simply getting lucky.
Typically, the heap is contiguous. This means that when you write past the
malloc
ed memory, you could be corrupting the following memory block, or some internal data structures that may exist between user memory blocks. Such corruption often manifests itself long after the offending code, which makes debugging this type of bugs difficult.You're probably getting the
NUL
s because the memory happens to be zero-filled (which isn't guaranteed).正如@Als 所说,这是未定义的行为。这可能会崩溃,但不一定。
许多内存管理器分配较大的内存块,然后以较小的块(可能是 4 或 8 字节的倍数)将其交给“用户”。因此,您在边界上的写入可能只是写入分配的额外字节。或者它会覆盖您拥有的其他变量之一。
As @Als said, this is undefined behaviour. This may crash, but it doesn't have to.
Many memory managers allocate in larger chunks of memory and then hand it to the "user" in smaller chunks, probably a mutliple of 4 or 8 bytes. So your write over the boundary probably simply writes into the extra bytes allocated. Or it overwrites one of the other variables you have.
你没有
malloc
-ing足够的字节。第一个字符串"123456789"
为 10 个字节(存在空终止符),并且{'h','e','l','l','o'}
是 6 个字节(再次为空终止符腾出空间)。您当前正在使用该代码破坏内存,这会导致未定义(即奇怪)的行为。You're not
malloc
-ing enough bytes there. The first string,"123456789"
is 10 bytes (the null terminator is present), and{'h','e','l','l','o'}
is 6 bytes (again, making room for the null terminator). You're currently clobbering the memory with that code, which leads to undefined (i.e. odd) behavior.