碰到一道题目:
char s[]="123456789";
char d[]="123";
strcpy(d,s);
cout<<s<<endl;
cout<<d<<endl;
问输出结果。
答案是比较奇怪的
按常理应该是两者都输出123456789。strcpy()的函数原型为char * __cdecl strcpy(char *, const char *),从网上流出的strcpy()源码无法分析出端倪,考虑到本例唯一的特殊点在于违背了strcpy()要求目的字符串要有足够空间容纳复制的内容的规定。所以从此处入手。
在VC++6.0编译器下调试后发现
执行strcpy()前:
strcpy后:
从两张图中可以看出&d[3]+1处的内存地址便是s[]的起始地址,所以得出一种假想,编译器因为d[]中本无法存放‘5’-‘\0’的6位字符,而本例中d[]与s[]分配的内存连续,即复制s[]到d[]时数组赋值发生越界,借用了接下来的内存物理地址0x0013ff74-0x0013ff79(也就是s[0]-s[5]的空间)存放来‘5’-‘\0’。而输出的s[]时由于输出函数碰到‘\0’即终止,所以只输出56789。
为验证这种假想,尝试在s[]和d[]之间栈上分配其他内存或者颠倒d[]和s[]的定义顺序以破坏这种内存的连续性,输出结果果然为正常显示了。
到此似乎得出一种结论,当strcpy()的目的字符串定义紧跟在源原字符串定义后面时,由于“分配的内存相连续”,目的字符串d[]的输出结果正常,而源字符串s[]的输出结果会是从s[strlen(d)+1]往后的字符。若两者分配的内存真是相连,因为会发生覆盖且输出函数是碰到‘\0’就结束相信会真的如此结论所说,但是对定义顺序相邻近就内存分配连续有所怀疑,对此做了下面的实验。
只是改动了下d[]数组分配的空间大小为5B,则输出结果不一致。
调试后发现:
strcpy前:
strcpy 后:
由图中可推测,d[]和s[]虽定义相邻但未内存相连续,d[0]-d[4]分别存放字符‘1’-‘4’,而在接下来的内存0x0013ff71-0xff001373中分别复制存放字符‘5’-‘8’,从而在内存0x0013ff74-0x0013ff75也就是s[0]和s[1]中覆盖存放‘9’和‘\0’,所以自然输出s[]为9。
继续实验后发现尝试给d分配的内存为6,7,8时s[]均输出为9,并不是很随机,调试后原因相仿,而当给d[]分配的内存>=9时则输出结果为123456789正常显示。
其中似乎有一种巧合性,在分配给目的字符串d[]的空间,大于实际数据的大小(包括‘\0’,本例中“123”为4,定义为d[]或d[4])且小于源字符串strlen(s)(不包括‘\0’,本例为9)时,编译器给相邻两者分配空间之间的间隙总是正好足够复制到倒数第三位字符(也仅是本例),本例中即可以复制到字符‘8’,而字符‘9’和‘\0’则需要借s[]的空间复制。
而当s[]改为“12345678”时,即strlen(s)=8时,给d[]的内存空间为5,6,7时,s[]的输出结果都为空,从上面可推测出,此时在"分配给目的字符串d[]的空间大于实际数据的大小且小于源字符串strlen(s)"时,编译器给相邻两者分配空间之间的间隙该总是正好足够复制到倒数第二位字符,所以在s[0]处被覆盖上了'\0',而给d的空间为本身大小即改为char d[4]="123"或char d[4]="123",依然是我们上面所推测的“从s[strlen(d)+1]往后的字符”(5678)。
依此可推想,再缩小一字节s[]的空间如下,定义d[5]或d[6]="123"时该不覆盖s[]的空间了,也就是输出按常理显示;或者增加1字节s[]的空间,定义为 char s[]="1234567890",d[5]或d[6] d[7] d[8] 或d[9]="123",s[]均输出90:实验结果也确实如此。其中似乎有另一种规律性。
总结:
strcpy()函数当目标字符串d[]没有足够内存容纳复制时会发生赋值数组越界,这种方式显然不值得提倡,不过编译器并未报错,d[]始终会正常输出为原先的s[],s[]输出结果未知但部分也隐含着某种规律性:
先看目的字符串d[]定义是否紧跟在源原字符串s[]定义后面(栈上定义在后的变量地址值小),
若不是,复制时自然越界不到s[]所在内存,s[]则按原先内容显示;
若是,再看给d[]分配的空间是否正好为strlen(d)+1,
是的话,s[]输出为原先“从s[strlen(d)+1]往后的字符”,
若也不是,看给d[]分配的空间是否>=strlen(s),大于的话仍是按原先内容显示,
若间于两者之间,由于内存覆盖情况未知,s[]的输出无法判断。(虽然似乎也有某种规律性 :
给d[]分配的空间在这个区间内虽不同,覆盖情况却相同从而s[]输出也相同。)