1

在第一章中,K&R 介绍了一个函数拷贝如下:

void copy(char to[], char from[]) {
/* copy from from[] to to[], assumes sufficient space */
    int i = 0;
    while ((to[i] = from[i]) != '\0') {
        i++;
    }
}

稍微修改一下这个函数,我得到了一些意想不到的结果。示例程序:

int main() {
    char a[3] = {'h', 'a', '\n'};
    char b[3];
    printf("a: %s", a); // prints ha
    copy(b, a);
    printf("a: %s", a); // prints nothing
    printf("b: %s", b); // prints ha

    return 0;
}

现在我的问题:

  1. 为什么从复制ab工作,这就是为什么即使a不包含'\ 0',复制中的while循环也会终止?

  2. 为什么会a变异?

4

5 回答 5

3

您可能遇到缓冲区溢出。

由于a没有正确终止(缺少\0),只要找到 no ,就从tocopy复制。因此,写入的字节更多,因为它可以包含然后溢出到(平台相关,未定义的行为)。ab\0ba

溢出的部分是\0after a,因此形成a了一个零长度的字符串。

您的堆栈可能如下所示:

                        b        a        
[ arbitrary memory ][ 0 0 0 ][ h a \n ][ 0 ? ? ? ? ]

表示未知数据,?因为我们不知道那里有什么,也没有指定。但是我们知道必须有一个0否则printf会打印更多的垃圾。

copy复制直到有一个零之后才发现开始a。碰巧 0在 的末尾有一个 a a,然后将其复制到b。由于b已经充满了来自 的内容ab溢出到a,使您的堆栈看起来像这样:

                        b         a
[ arbitrary memory ][ h a \n ][ 0 a \n ][ 0 ? ? ? ? ]

由于\0的开头有 a a,因此printf假定a为空。

于 2013-08-20T15:08:08.353 回答
2

copy函数依赖于终止的空字节来确定何时应该停止复制。当您使用字符串常量时,它会自动以空值结尾。但是,普通数组不是以空值结尾的,因此该函数会继续访问末尾的内存,a直到其中一个字节恰好是\0. 函数何时copy停止复制取决于该内存区域的内容。你不知道发生了什么,如果有的话,所以你不知道要继续复制多久copy或会发生什么。

于 2013-08-20T15:00:22.997 回答
1

即使您没有明确地将 a'\0'放入您的变量a中,也很可能a[3] (技术上超出范围恰好是零。

很多内存通常都用零/\0值填充,尽管绝对不能保证这一点。

这就解释了为什么您成功地将字符串复制到b.

您的复制函数在它“认为”其复制数组的意义上是错误的ints

void copy(int to[], int from[]) {

当它应该复制演员数组时char

void copy(char to[], int char[]) {
于 2013-08-20T14:56:20.837 回答
1

它有效,因为您的 while 循环没有执行逻辑操作,而是执行分配。所以只要指定的值是真的,它就可以通过循环。(只要它不为零)所以它在达到'\ 0'(什么是0)时终止。即使没有'\ 0',它也可能随时终止(具有未定义的行为),因为在离开边界后,任何零值字节出现的机会都很大。但是在离开数组边界之后,程序的行为可能是任何东西。(它甚至可以让鼻龙产生;)) b 不打印任何内容的原因可能是基于字节顺序,因为您将 int 值写入字节数组,所以可能是 Byteorder 的第一个字节被放置在第一个字节中并且是 0 所以从外部它将被视为 '\0' 即使您的目的是将整个 int 视为一个符号而不是 4 个单价值观。ps:这甚至会破坏严格的别名规则。

于 2013-08-20T15:00:08.707 回答
1

最好在下面的 while 循环之前检查tofrom是否为 NULL。

while ((to[i] = from[i]) != '\0'){ i++; }

对于您的问题:

1.循环终止,因为在a之后存储在内存中的东西等于0(或'\0'),这是未定义的。

2.在调用未定义的副本后, a可能会改变或不改变。两个变量都存储在a之后,位置b存储在 effect 之后。在上面提到的 nemo 案例中,执行了一个mutated after 函数复制

为了安全起见,始终在 char 数组的末尾插入一个 '\0'。

于 2013-08-20T16:22:51.160 回答