C中的字符串复制错误

时间:2018-08-19 06:33:57

标签: c

我不明白为什么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为什么要在这里更改?

3 个答案:

答案 0 :(得分:2)

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

堆栈上的缓冲区溢出

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

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

答案 1 :(得分: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'。另外,请注意,由于这是未定义的行为,并且未定义两个字符串的存储布局,因此输出可以是任何内容。

答案 2 :(得分:0)

该程序的结果未知,可能各种各样,原因是: 使用str3和str4时不进行初始化,最好像这样进行初始化:

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

因此,遵守40号尺寸的边框将在程序员的控制下。

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