《C语言36—不同数据类型的strcpy》

2019年4月9日星期二 中雨转小雨


在微信公众号里看见一个题目,自己来试了一下,于是将其做一个记录。
公众号的名称是“C语言与程序设计”,原文链接是:一道C语言面试题,真的不简单!


原程序是:

#include 
#include 

int main(void)
{
    char s[] = "abcdefghijklmnopqrstuvwxyz";
    char d[] = "123";
    strcpy(d, s);
    printf("%s %s", s, d);

    return 0;
}

我在vs2017里面将strcpy格式改成strcpy_s后变成:

#include 
#include 

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日

你可能感兴趣的:(《C语言36—不同数据类型的strcpy》)