【问题标题】:String copy error in CC中的字符串复制错误
【发布时间】:2019-01-25 15:53:06
【问题描述】:

我不明白为什么 str1 会在这里发生变化。请帮忙。

#include<stdio.h>
#include<string.h>
int main (){
    char str1[]="stackoverflowwwwwww";//str1
    char str2[] = "stackoverflow";
    char str3[40];
    char str4[40];
    char str5[] = "asdfgh";
    strcpy(str2, str1);
    strcpy(str3, "successful");
    strcpy(str4, str5);
    printf("str1: %s\nstr2: %s\nstr3:%s\nstr4:%s\n", str1, str2, str3, str4);
    return 0;
}

输出是:

str1: www
str2: stackoverflowwwwwww
str3: successful
str4: asdfgh

为什么str1 在这里变了?

【问题讨论】:

  • 您正在调用未定义的行为strcpy(str2, str1); 目标缓冲区有多大?你的问题的答案就是那个问题的答案。
  • @Cplusminus_is_coming 你能用它的规范引用来支持这个期望吗(例如en.cppreference.com/w/c/string/byte/strcpy)?或者您是否正在考虑使用适当参数的 strncpy?
  • 能否提供您环境中地址差异str1-str2的值?要么使用调试器,要么打印指针值。
  • 如果您真的很好奇为什么会发生这种情况在您的特定情况下,请使用您的调试器。就语言而言,没有已定义的原因。一旦调用未定义的行为,您就离开了该保留的安全性。
  • 使用[40] 而不是[] .strcpy 足够安全

标签: c


【解决方案1】:

您的程序包含缓冲区溢出。 str2 是一个缓冲区,它的大小是 14 字节(“stackoverflow”的长度 + 终止 NULL 字符)。当您执行strcpy(str2, str1) 时,您将溢出该缓冲区,因为str1 超过14 字节。 strcpy 无法知道缓冲区的大小,因此(根据其定义)它会盲目地从一个缓冲区复制到另一个缓冲区。

堆栈上的缓冲区溢出

现在,为什么这会改变str1?好吧,因为当strcpy 复制的字符多于目标缓冲区的大小时,这些字符必须去某个地方。 由于str2str1 都分配在堆栈上,因此它们彼此相邻放置。因此,当 strcpy 复制到 str2 并溢出其大小时,它将移动到内存中它旁边的任何内容 - 在您的情况下是 str1

因此,strcpy 的最后一个字符正在复制 - str1 中的最后几个字符 - 被复制回 str1 的开头。

【讨论】:

    【解决方案2】:

    首先,目标str2 比字符串长度str1 短,因此这是一个未定义的行为。

    但要了解为什么会发生这种情况,请考虑以下事项。

    当我打印出字符串的内存地址时,在我的系统中对你的代码进行任何修改之前都是这样的。

    printf("str1: %p (%d)  \nstr2: %p (%d)\n", str1, strlen (str1),  str2, strlen (str2));
    
    str1: 0x7ffd394e85d0 (19)  
    str2: 0x7ffd394e85c2 (13)
    str1: wwwww
    str2: stackoverflowwwwwww
    str3:successful
    str4:asdfgh
    

    注意str1str2的地址。 str2 首先开始(低地址)和0x7ffd394e85d0 - 0x7ffd394e85c2 = e,这是十进制的 13,字符串的确切长度。

    因此初始布局是

    Initial layout
    0x7ffd394e85c2               0x7ffd394e85d0
    str2                         str1
    |                            | 
    |                            |
    V                            V
    s t a c k o v e r f l o w \0 s t a c k o v e r f l o w w w w w w w \0
    

    当字符串从str1 复制到str2 时,strcpy 将按如下方式对齐和复制字符串。这里str2缓冲区的结尾被违反了。

    When being copied
    0x7ffd394e85c2               0x7ffd394e85d0
    str2                         str1
    |                            | 
    |                            |
    V                            V
    s t a c k o v e r f l o w \0 s t a c k o v e r f l o w w w w w w w \0
    s t a c k o v e r f l o w w  w w w w w \0
    

    复制后状态如下

    After copy
    0x7ffd394e85c2              0x7ffd394e85d0
    str2                        str1
    |                           | 
    |                           |
    V                           V
    s t a c k o v e r f l o w w w w w w w \0 v e r f l o w w w w w w w \0
    

    现在str1 指向以'w' 开头的缓冲区的开头,它有五个连续的'w' 直到它到达'\0'。因此,str1 指向的 C 字符串现在是“wwwww”,正如您在输出中看到的那样。

    注意,这适用于我的系统,在该系统中我得到了五个连续的 'w'。在其他系统中,编译器可能会生成在str2 的结尾和str1 的开头之间有几个空白字节的代码,因此它们可以有不同数量的'w'。另外请注意,由于这是一个未定义的行为,并且两个字符串的存储布局未定义,因此输出可以是任何内容。

    【讨论】:

      【解决方案3】:

      这个程序的结果是未知的,可能是各种各样的,因为: str3 和 str4 不用初始化,最好这样初始化:

      char str3[40] = {0};
      char str4[40] = {0};
      

      因此,大小为 40 的边框将由程序员控制。

      很明显,str2的空间如果执行'strcpy(str2, str1);'就会出问题

      【讨论】:

        猜你喜欢
        • 2012-10-29
        • 1970-01-01
        • 1970-01-01
        • 2012-10-25
        • 2020-06-29
        • 2019-03-18
        • 1970-01-01
        • 1970-01-01
        • 2019-03-26
        相关资源
        最近更新 更多