为什么故意不正确地使用strcpy不会失败呢?
为什么使用strcpy
的下面的C代码对我来说效果很好? 我尝试以两种方式使其失败:
1)我尝试从字符串文字strcpy
到分配的内存太小,不能包含它。 它复制了整个事情并没有抱怨。
2)我从一个非NUL
-terminated的数组中尝试了strcpy
。 strcpy
和printf
工作得很好。 我曾经以为strcpy
复制了char
直到找到NUL
,但没有一个存在且仍然停止。
为什么不这些失败? 我是以某种方式获得“幸运”,还是我误解了这个function是如何工作的? 它是特定于我的平台(OS X Lion),还是大多数现代平台以这种方式工作?
#include #include #include 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
首先,复制到太小的数组:
C对于超越数组边界没有任何保护,所以如果在dst1[5..9]
没有任何敏感,那么你很幸运,副本会进入你不正确拥有的内存,但它不会崩溃无论是。 但是,该内存不安全,因为它尚未分配给您的变量。 另一个变量可能会分配给它的内存,然后覆盖你放在那里的数据,以后破坏你的字符串。
其次,从非空终止的数组复制:
即使我们经常被教导内存中充满了任意数据,但它的大部分都是零。 即使你没有在src2
放置一个null-terminator,但是src[5]
恰好是\0
可能性很大。 这使得复制成功。 请注意,这不保证,并且可能在任何时候在任何平台上运行失败。 但是这次你很幸运(可能大部分时间都是这样),而且它很有用。
覆盖超出已分配内存的范围会导致未定义的行为 。
所以在某种程度上,你很幸运。
未定义的行为意味着任何事情都可能发生,行为无法解释为定义语言规则的标准,不定义任何行为。
编辑:
在第二个想法,我会说你真的不走运 ,这个程序工作正常,不会崩溃。 它现在起作用并不意味着它总会起作用,事实上它是一个炸弹炸毁的炸弹。
根据墨菲定律 :
“ 任何可能出错的事情都会出错 ” [
“并且很可能在最不方便的时刻” ]
[
]
– 我的法律编辑:)
是的,你很幸运。
通常,堆是连续的。 这意味着当您通过malloc
ed内存写入时,可能会破坏以下内存块或用户内存块之间可能存在的某些内部数据结构。 这种损坏通常在违规代码之后很久就会出现,这使得调试此类错误变得困难。
你可能得到了NUL
因为内存恰好是零填充(不能保证)。
正如@Als所说,这是未定义的行为 。 这可能会崩溃,但它不必 。
许多内存管理器分配更大的内存块,然后以较小的块将其交给“用户”,可能是4或8字节的多个块。 因此,您对边界的写入可能只是写入分配的额外字节。 或者它会覆盖您拥有的其他变量之一。
你没有在那里使用足够的字节。 第一个字符串"123456789"
是10个字节(存在空终止符), {'h','e','l','l','o'}
是6个字节(同样,为null终止符)。 您目前正在使用该代码破坏内存,这会导致未定义(即奇怪)行为。