为什么故意不正确地使用strcpy不会失败呢?

时间:2011-08-21 17:29:43

标签: c malloc strcpy

为什么使用strcpy的以下C代码对我来说效果很好?我尝试以两种方式使其失败:

1)我从字符串文字中尝试strcpy到分配的内存中,该内存太小而无法包含它。它复制了整件事并没有抱怨。

2)我从一个不是strcpy的数组中尝试NUL - 终止了。 strcpyprintf工作得很好。我原以为strcpy复制char s直到找到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

5 个答案:

答案 0 :(得分:18)

首先,复制到太小的数组:

C对于超越数组边界没有任何保护,所以如果dst1[5..9]没有任何敏感信息,那么你很幸运,并且副本会进入你不正确拥有的内存中,但它没有也崩溃了。但是,该内存不安全,因为它尚未分配给您的变量。另一个变量可能会分配给它的内存,然后覆盖你放在那里的数据,以后破坏你的字符串。

其次,从非空终止的数组中复制:

尽管我们经常被教导说内存中充满了任意数据,但是大量的内存都是零。即使没有在src2中放置空终结符,但src[5]恰好是\0的可能性也很大。这使得复制成功。请注意,这是 NOT 保证,并且可能在任何平台上随时在任何运行中失败。但是这次你很幸运(可能大部分时间都是这样),而且它很有用。

答案 1 :(得分:14)

超出分配内存范围的覆盖会导致未定义行为 所以在某种程度上,你很幸运。

未定义的行为意味着任何事情都可能发生,并且无法解释行为,因为定义语言规则的标准没有定义任何行为。

修改
在第二个想法,我会说你真的不幸,这个程序工作正常,不会崩溃。它现在起作用并不意味着它总会起作用,事实上它是一个炸弹炸毁的炸弹。

根据 Murphy's Law
 “ 任何可能出错的内容都会出错 [“并且最有可能出现在最不方便的时刻”{{1} }

] [ - 我的法律编辑:)

答案 2 :(得分:4)

是的,你很幸运。

通常,堆是连续的。这意味着当您通过malloc内存写入时,可能会破坏以下内存块或用户内存块之间可能存在的某些内部数据结构。这种损坏通常在违规代码之后很久就会出现,这使得调试此类错误变得困难。

您可能正在获取NUL,因为内存恰好是零填充(无法保证)。

答案 3 :(得分:4)

正如@Als所说,这是未定义的行为。这可能会崩溃,但没有

许多内存管理器分配更大的内存块,然后以较小的块(可能是4或8字节的多个块)将其交给“用户”。因此,您对边界的写入可能只是写入分配的额外字节。或者它会覆盖您拥有的其他变量之一。

答案 4 :(得分:1)

你不是malloc - 那里有足够的字节。第一个字符串"123456789"是10个字节(存在空终止符),{'h','e','l','l','o'}是6个字节(同样,为空终止符腾出空间)。你现在正在使用该代码破坏内存,这会导致未定义(即奇怪)的行为。