K& R代码示例混淆:复制字符串

时间:2013-08-20 14:53:55

标签: c arrays string

在第一章中,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的复制有效,这就是为什么复制中的while循环终止,即使a不包含'\ 0'

  2. 为什么a发生变异?

5 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

您可能正在遇到缓冲区溢出。

由于a未正确终止(\0},copya复制到b,只要它找不到\0 }。因此,有更多的字节写入b,因为它可以包含,然后溢出到a(平台相关的,未定义的行为)。

溢出的部分是\0之后的a,从而使a为零长度字符串。

您的筹码可能如下所示:

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

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

copy副本,直到开头a后找到零。偶然的,有一个 0结束后a,然后将其复制到b。由于b已经填满了 来自ab的内容溢出到a,使您的堆栈看起来像这样:

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

由于\0开头有aprintf假定a为空。

答案 2 :(得分:1)

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

很多内存通常都填充了零/ \0值,但绝对无保证

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

您的复制功能在“认为”其复制数组ints

的意义上是错误的
void copy(int to[], int from[]) {

什么时候应该复制char acters的数组:

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

答案 3 :(得分:1)

它可以正常工作,因为你的while循环没有进行逻辑运算,它正在做一个对齐。 因此,只要asigned值为真,它就会经历循环。(并且只要它不为零)所以当达到'\ 0'(whats 0)时终止它。 即使没有'\ 0',它也可能会终止(具有未定义的行为),因为在离开你的边界后,任何零值字节的出现几率都很大。但在离开阵列边界后,程序的行为可能是任何东西。 (它甚至可以让鼻龙产卵;)) b不打印任何内容的原因可能是以字节顺序为基础,因为您将int值转换为字节数组,因此可能是Byteorder的第一个字节放在第一个字节中,因此从外部是0它将被视为'\ 0',即使你的目的是将整个int视为符号而不是4个单值。 p.s。:这甚至会破坏严格别名规则。

答案 4 :(得分:1)

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

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

问题:

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

2. a 可能会在调用 copy 之后发生变化,这也是未定义的。两个变量都存储在 a 之后,位置 b 存储在效果中。在上面提到的nemo案例中, a 在执行复制功能后发生了变异。

为了安全起见,总是在char数组的末尾插入'\ 0'。