2019年4月9日星期二 中雨转小雨
在微信公众号里看见一个题目,自己来试了一下,于是将其做一个记录。
公众号的名称是“C语言与程序设计”,原文链接是:一道C语言面试题,真的不简单!
原程序是:
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[] = "abcdefghijklmnopqrstuvwxyz";
char d[] = "123";
strcpy(d, s);
printf("%s %s", s, d);
return 0;
}
我在vs2017里面将strcpy格式改成strcpy_s后变成:
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[] = "abcdefghijklmnopqrstuvwxyz";
char d[] = "123";
strcpy_s(d,100, s);
printf("%s %s", s, d);
return 0;
}
运行程序之后出现如下情况:
从显示的结果看,运行结果是:
mnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz
但是在运行结果出来后却报错了:
Run-Time Check Failure #2 - Stack around the variable 's' was corrupted.
s是错误的?
(我和这篇文章的作者的运行结果有所不同,据说是因为具体分配内存有可能不一样。)
以下是作者的说法:
请看题:
题目描述很简单,让你分析它的输出。
- 小伙伴A说,这不就是把s复制到d嘛,所以输出自然是s不变,d变为s了啊。
- 小伙伴B说,很明显复制到d时越界了啊,程序会报错然后停止运行啊。
- 小伙伴C说,应该没有表面上越界这么简单,我得好好思考一下,肯定会有陷阱,等我调试一下再给出答案。
以上是3种说法,我们先不评论,直接给出程序运行结果:
咦?很奇怪,输出d是正确的,s发生了截断!要说越界也应该是d错啊,这是什么情况?大家不要着急,我们一步步来分析调试找原因。
首先我们先来回顾一下strcpy函数的原理,把一个字符串复制到一个字符串上并在末尾追加空字符,但没有越界检验,安全性堪忧。但此题看运行结果是复制成功了,不应该是越界吗?
那再往下就不能靠分析了,得调试程序找错了,说白了现在问题就在越界这里,似乎得用我们的利器printf查看字符串存储的内存地址了,上图:
看到内存地址我们好像明白了点什么,d的起始地址为20,s起始地址为30,也就是说系统并不是跟我们想像的那样给数组d只分配了4字节的内存,而是分配了16字节(内存地址为16进制)!
到这里,基本就全明白了,应该是把s复制到d中时,26个字母占用26字节确实越界了,占用了s本身的一些存储空间,d原来的16字节空间存了到p的16个字母,从q开始剩下的10个字母把原先s中的前十一个字母覆盖了(因为strcpy还追加了空字符),当最后打印%s 时,直到空字符打印才停止,因此d打印正确,s只打印了从q开始的10个字母,我们把s改为10个字母来验证一下我们的推测:
结果正确!当然,具体给d分配多少空间可能取决于编译器和系统的具体实现,但可以肯定的是只要不够27字节,s肯定会发生截断,而且在d仅为字符串123的前提下,d的空间99%不会达到27字节,也就是s很大几率要截断。
此题还有一点没讲,如果把s和d的那两行代码交换一下位置会发生什么呢?这个就留给大家吧,提醒一下没那么简单哦!
以下是我的调试及后续调整的运行过程与结果:
调试的结果是:
004FFC54 004FFC48
mnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz
在结果出来后,也还是报错了。
后面的过程我还没怎么看懂,先搁置吧。
2019年4月9日