代码之家  ›  专栏  ›  技术社区  ›  timgeb

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

  •  1
  • timgeb  · 技术社区  · 13 年前

    在第一章中;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. 为什么从 a b 工作,这就是为什么复制中的while循环会终止,即使 不包含“\0”?

    2. 为什么 变异?

    5 回复  |  直到 12 年前
        1
  •  3
  •   nemo    13 年前

    您可能遇到缓冲区溢出。

    a 未正确终止(缺少 \0 ), copy 副本来自 b 只要它发现没有 \0 。因此,写入的字节更多 b 因为它可以包含,然后溢出到 (依赖于平台,未定义的行为)。

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

    您的堆栈可能如下所示:

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

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

    复制 复制,直到找到零 之后 开始 。碰巧有一个 0 结束后 ,然后将其复制到 b b 已填充 内容来自 , b 溢出到 ,使堆栈看起来像这样:

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

    因为有一个 \0 在开始 , 输出函数 假设 空的。

        2
  •  2
  •   thejh    13 年前

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

        3
  •  1
  •   abelenky    13 年前

    尽管您没有明确地 '\0' 在变量中 a ,很可能 a[3] (从技术上讲 禁止进入 ) 恰好是一个零。

    很多内存通常都是零/ \0 价值观,尽管绝对存在 无担保 这一点。

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

    您的复制函数是错误的,因为它“认为”它的复制数组 ints :

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

    何时应该复制的数组 char 演员:

    void copy(char to[], int char[]) {
    
        4
  •  1
  •   dhein    13 年前

    它是有效的,因为while循环不是在做逻辑运算,而是在做一个旁白。 因此,只要给定的值为真,就需要经过循环很长时间。(只要它不是零)所以当到达“\0”(whats 0)时它就终止了。 即使没有“\0”,它也可能在任何时候终止(具有未定义的行为),因为在离开边界后,出现任何零值字节的可能性都很大。但是在离开数组边界之后,程序的行为可以是任何东西。(它甚至可以让鼻龙产卵;) b不打印任何内容的原因可能是基于Byte顺序,因为您正在将int值写入Byte数组,所以可能是Byteorder的第一个Byte放在第一个字节中,并且是0,所以从外部来看,它将被视为“\0”,即使您的目的是将整个int视为一个符号,而不是4个单个值。 附言:这甚至会打破严格的别名规则。

        5
  •  1
  •   sensor    13 年前

    最好检查一下 从…起 在下面的while循环之前为NULL。

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

    对于您的问题:

    1.循环终止,因为在 等于0(或“\0”),这是未定义的。

    2. 打电话后可能会更改或不更改 复制 这也是未定义的。两个变量都存储在 以及位置 b 在生效时存储。在上述nemo的情况下, 功能后突变 复制 被处决。

    为了安全起见,请始终在字符数组的末尾插入一个“\0”。