
着实无法理解,为什么已经赋值的 c 和 d 会在 strcpy 之后改变原值(这里故意设置错误下标导致复制越界)
#include <stdio.h> #include <string.h> int main(){ char a[6]={"hello"},b[7]={"world1"}; // a[]="hello"; // b[]="world"; int c,d; int *c_,*d_; c=(int)sizeof(a)/sizeof(*a); d=(int)sizeof(b)/sizeof(*b); // c_=&c; // d_=&d; // printf("c_=%p\td_=%p\n",c_,d_); // printf("c=%p\td=%p\n",&c,&d); printf("c=%d,d=%d\n",c,d); strcpy(a,b); printf("%s-%s\n",a,b); printf("c=%d,d=%d",c,d); } 运行结果: !run1 [root@iZwz94s0djlh8ob47gwodcZ Ctest]# ./test
c=6,d=7
world1-world1
c=6,d=0
#include <stdio.h> #include <string.h> int main(){ char a[6]={"hello"},b[7]={"world1"}; // a[]="hello"; // b[]="world"; int c,d; int *c_,*d_; c=(int)sizeof(a)/sizeof(*a); d=(int)sizeof(b)/sizeof(*b); c_=&c; d_=&d; // printf("c_=%p\td_=%p\n",c_,d_); // printf("c=%p\td=%p\n",&c,&d); printf("c=%d,d=%d\n",c,d); strcpy(a,b); printf("%s-%s\n",a,b); printf("c=%d,d=%d",c,d); } 运行结果:
[root@iZwz94s0djlh8ob47gwodcZ Ctest]# ./test
c=6,d=7
world1-world1
c=6,d=7
测试了 gcc 和 clang,只有 gcc 出现这个问题。

gdb里很明显了,因为越界导致顶栈被设为0。
想到一个曲线救国的方法,就是再另外设置一个变量 'e',只要数组复制相差的位数小于e,就可以假装显示正常结果,相当于设置一个变量当作溢出缓存,避免顶端想要的结果被修改,不过也只是可用在研究或调试上。
很低端的错误,让大家见笑了。
1 hello2060 2021 年 1 月 27 日 via iPhone 已经忘了 C 了,但是既然 stripy 越界了,变量值改变也是有可能的啊,你看看数组 a 和 d 的地址,看看 d 是不是跟在 a 数组元素后面 |
2 momo1999 2021 年 1 月 27 日 你不懂什么叫越界吗 |
3 hello2060 2021 年 1 月 27 日 via iPhone 最简单的,IDE 里单步 debug 看 memory 变化 |
4 hello2060 2021 年 1 月 27 日 via iPhone 最后你定义了 c_ d_那可能就改变了栈内各个变量地址的关系,本身正常的程序就不预设栈内变量地址之间的关系。一旦越界了,啥都有可能发生。 不过最简单的还是 debug 一下 |
5 hello2060 2021 年 1 月 27 日 via iPhone 你说你无法理解,这个不需要理解啊,因为这个 code 出错了,所以啥错误都是有可能的。 |
6 NoahNye OP @hello2060 谢谢回复,之前有发过在 stackoverflow,好像也差不多得到这种回答,大概是错误的代码导致不可预期的行为,但我还是觉得这个赋值的这两个变量不应该因为 strcpy 而改变,因为它们并没有在 strcpy 之后被重新赋值。既然是程序错误,我也不在这个错误代码里钻牛角尖了。最后再次感谢您的回复。 |
7 LANB0 2021 年 1 月 27 日 如一楼所说,你 strcpy 越界了。越界的字节覆盖了变量 d 低字节的内存,后一种写法,d_的值也是有误的。 Linux 下局部变量内存地址分布顺序: 按字节大小,大的先入栈; 字节大小相同的,后定义的先入栈; 按定义顺序,依次入栈的是 c,d,a,b ; intel 小端,d 的低字节和 a 的末尾是连着的;进而溢出的'\0'把 d 的低字节覆盖了 |
8 hello2060 2021 年 1 月 27 日 strcpy(a, b) 的时候 a + 6 和 a + 7 那个位置的值都被错误的覆盖了(我已经忘了最后的\0 是怎么处理的了,反正至少有一个 byte 被 strcpy 这个动作错误的改写了, d 的内存地址可能刚好包括这个 byte, '这个赋值的这两个变量不应该因为 strcpy 而改变,因为它们并没有在 strcpy 之后被重新赋值' -- 变量 d 并不是只有被重新赋值了才会有新值,任何操作他内存地址的操作都有可能改变他的值啊 比如说你定义 char a, char b, 假设他们在内存里是 [a,b] 你再定义 int* p = &a, (我不确定这个是否是正确的语法) *p = 0, 这里对 p 的内存改写,因为 p 指向 4 个 byte 的 int, 从 a 地址开始的 4 个 byte 都变成 0 了,b 也变 0 了 |
9 nightwitch 2021 年 1 月 27 日 继续往下学吧,如果你只停留在 C 语法上的话,大家跟你解释你也不大听得懂。 这个问题你学到 C 语言的基本类型在机器上的内存排布就自然明白了 |
10 changcui 2021 年 1 月 27 日 和 strcpy 无关吧,sizeof 是编译的时候就计算好的 |
11 ipwx 2021 年 1 月 27 日 怎么又讨论起未定义行为了?讨论这种的行为没有意义,因为 -O3 可以把指令都乱序,内存读取跳过(用寄存器),诸如此类的。。。 |
12 huangmingyou 2021 年 1 月 27 日 gdb 解君愁 |
13 ghostcir 2021 年 1 月 27 日 因为 a 内存越界了,结尾的\0 填到了 d 的内存 |
14 aneostart173 2021 年 1 月 27 日 第一个 c_和 d_都没用,编译器给优化掉了, 第二个 c_和 d_都用上了,栈布局就变了。越界了什么事都会发生,讨论这个没任何意义。 |