11

为什么下面的 C 代码使用strcpy对我来说很好?我试图通过两种方式使其失败:

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

2)我尝试strcpy了一个未NUL终止的数组。和工作strcpyprintf很好。我一直认为strcpy复制chars 直到NUL找到 a ,但没有存在并且它仍然停止。

为什么这些不失败?我只是以某种方式变得“幸运”,还是我误解了这个功能的工作原理?它是特定于我的平台(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
4

5 回答 5

19

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

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

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

尽管我们通常被告知内存中充满了任意数据,但其中很大一部分都被归零了。即使没有在 中添加空终止符src2,也很有可能src[5]发生这种情况\0。这使得复制成功。请注意,这并不能保证,并且可能在任何时间、任何平台上的任何运行中失败。但是这次你很幸运(可能是大部分时间),它奏效了。

于 2011-08-21T17:33:42.447 回答
14

超出分配内存范围的覆盖会导致Undefined Behavior
所以在某种程度上,是的,你很幸运。

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

编辑:
再想一想,我想说你真的很不幸,程序运行良好并且不会崩溃。它现在有效并不意味着它会一直有效,实际上它是一颗正在滴答作响的炸弹。

根据墨菲定律
任何可能出错的事情都会出错[“而且很可能在最不方便的时刻”]

[ ]- 是我对法律的编辑吗:)

于 2011-08-21T17:31:47.600 回答
4

是的,你很幸运。

通常,堆是连续的。这意味着当您写入超过malloced 内存时,您可能正在破坏以下内存块,或者可能存在于用户内存块之间的一些内部数据结构。这种损坏通常在有问题的代码之后很长时间才显现出来,这使得调试这类错误变得困难。

您可能会得到NULs ,因为内存恰好是零填充的(不能保证)。

于 2011-08-21T17:33:51.130 回答
4

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

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

于 2011-08-21T17:34:54.497 回答
1

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

于 2011-08-21T17:34:07.350 回答