C语言中字符数组越界会怎样?

C语言探索 专栏收录该内容
4 篇文章 0 订阅

我们都知道C语言中的数组是不允许动态调整程度的,所有的数组都必须在声明的时候就指定它的长度,比如:

char str[]

就会报错: [Error] storage size of ‘str’ isn’t known
提示我数组的大小是未知的。事情是这样发生的,那是风和日丽的上午,我在家写代码,涉及到了C语言文件系统的内容,我用了一个 fgets()函数,熟悉C语言的盆友都知道,这是一个从指定的文件中读出一个字符串的函数,它的用法是:

fgets(字符数组名,n,文件指针名);

它的功能是从文件读出不超过n-1个字符,在读出的最后一个字符后面加上串结束标志**’\0’**,然后存到指定的字符数组里面去,这个函数会返回字符数组的首地址。
我设置了一个长度为10的字符串,char str[10];然后fgets(str,10,fp);没问题,这个操作成功地把文件里面的9个字符组成的串读出存到str数组str[0]~str[8]里面,str[9]会存放\0。使用printf可以打印出9个字符。然后我突发奇想,把str声明为大小只有5的字符数组:

char str[5]

按道理数组应该放不下9个字符,但是我发现字符数组越界后还是程序还是能正常允许。使用printf%s打印str字符数组还是会打印9个字符,就和str是长度为10的字符数组没有声明两样,编译过程也没有任何警告或者报错,就好像字符数组被拓展了。思考了一会后,我做了一些推论:

我们知道C语言在存储字符数组的时候,比如str[10],会选择一个连续的10个字节的空间,然后在第11个字节的位置自动加上一个’\0’,所以字符数组所占的实际大小总是比它能存储的字符数量+1.但是其他类型数组没有机制,这种特殊的机制之所以会出现,是因为字符数组常常需要被遍历。比如当我们遍历int数组的时候,我们会写一个for循环,我们也可以用这样的方法遍历字符数组。但是正是由于字符数组被遍历的可能性非常大,每次都写一个for是在不方便,于是printf提供了s格式来输出字符字符。这里就值得我们注意了,我们使用for来进行数组遍历的时候,循环终止条件需要我们手动设置,也就是说要什么来控制数组不越界。说明数组在存的时候,它是不知道自己有多长的。使用printf却可以不需要我们控制终止条件而不会越界,就是靠自动添加的\0实现的,从这里我们也可以看出来为什么printf只能输出字符数组,而不能输出其他类型的数组。还有一点需要注意,我们知道数组名其实都是数组首地址,我们可以

char *name;
name="hello world";

但是

char *name,str[5];
//name=str;
scanf("%s",name);

是不可以的。还是上面的\0机制造成的。name是一个指向字符类型的指针变量。“hello world“是一个字符数组常量,它已近被初始化了占用11+1个字节,最后一个字节存的就是\0,使用name数组指向它的首地址是OK的。第二个情况不行是因为name这个字符指针没有被初始化,它指向随机地方,使用未初始化的指针是相当危险的操作,所以编译试过不了的。加上name=str;就可以了我们已近知道printf打印字符数组就是靠自动\0机制来控制不越界的。想想下面一个场景:

char str[5];
scanf("%s",str);//只输入abc
printf("%s",str);

str有五个可用字节,\0存在第六个字节位置,那我们使用printf打印的时候,应该输出五个字节的内容啊,前面三个字节被打印为abc,后面两个字节将会是随机的,大概率乱码,因为不知道这两个字节之前存的是什么。但是现实中为什么不会发生这样的情况。我猜想就是因为scanf会在你输入完回车后,自动再加上一个\0,在这个例子中,这个\0就被存在第四个字节的位置了。

看到这里,前面的fgets函数自动加\0的目的我们知道了,就是为了保证printf这个字符数组的时候不会越界。我们为什么str只有5却能存10个字符,而且程序正常允许,还是因为fgets这个函数自动\0。这个字符数组的大小本身是没有变化的,我们使用sizeof来看的话还是5,str的实际存储情况是下面样子:
a b c d e|f g h i j \0
前面五个是字符数组str占用的空间,本来f的位置是\0,被我们通过字符数组越界改写为了f,然后通过fgets的机制再第十一个字节的位置加了\0,所以对于printf来说,它是找\0来防止越界的,导致能正常输出是个字符。我能通过上面也知道了scanf也能自动加\0,那能不能通过scanf来实现上面的骚操作呢,我们来实验一下:

char h[5];
scanf("%s",h)//输入hellohello
printf("%s\n",h);

结果也能正常打印hellohello出来,看起来我们是在越界,实际上我们就是再越界我们甚至可以用h[6]来找到e,但是我们要知道后面的字节被我们使用了,但是系统并不知道,因为我们只申请了5个字节,系统给了6个字节(自动加\0),而实际上我们用了13个字节(主动用了12个,scanf机制又用一个)。后面的字节再没有通知系统的情况下被我们使用,系统还不知道。这样做有多危险就不用说了,如果那些位置本身被存储了其他信息,就会导致那些信息被篡改。系统将会出现无法理解的无法预测的错误。
所以说,尽管通过实验我发现字符数组越界使用的方法—C语言针对字符数组自动补\0的机制。但是这样做仍然是非常危险的。

  • 8
    点赞
  • 0
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 护眼 设计师:闪电赇 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值