数组的溢出不可忽视

2019-04-15 12:08发布

c问题—关于数组溢出的思考


今天在做题时,发现定义整型数组a[10],并赋初值,然后我访问a[11],输出0,再a[11]=3后访问a[11],输出3,a的长度还是10,那么a[11]存在哪里了?


其实原问题下的回答已经能解决题主的问题了,只是自己也有些小思考,想着好久未写博客了,分享一点关于越界访问的经验。


首先,声明了int型数组a[10]的话,可知a指针指向RAM中一个区域,其后 10 x sizeof(int) 的区间就是数组a分配的空间。那么问题来了,如果我们访问 a + 11 x sizeof(int) 的地址(访问此地址C语言不会做越界检查),会得到什么?


以下回答基于芯片硬件设计与固件设计的经验总结,假设写访问为*a[11] = 0xXXXX,读访问为 b = *a[i] 。


RAM
一般情况下,这个地址仍然指向RAM,只是我们不清楚这个int长度的RAM存储单元被分配做什么用途。既然是RAM,当然是可读可写的。但是假设程序已经将此RAM空间分配做变量b的存储,如果我们对此地址进行读出操作,会将变量b的数值读出;如果对此地址进行写入操作,会导致b的数值更改。题主遇到的就是这种情况。


但是这样进行越界写入,会以一种很隐蔽的方式改变变量b的值。这会给后续debug引入很大的困难。所以CODING时,一定要避免越界访问。


NOR FLASH等可以通过指针读出但是无法写入的区域
在嵌入式芯片(比如STM32)中,常常使用NOR FLASH存储用户程序。


当a[11]指向NOR FLASH区域时,是可以通过指针操作进行读出的。但是如果想要进行写入操作,只是通过指针操作是不可行的,这种指针简单的写操作是无法改变其中的数据的。结果就是,可以读出a[11]的数据,但是写入后发现写入失败,仍保留原数值。


说到这里多说一句,若想写入NOR FLASH,硬件上需要将待写入的数据放入到一定大小的catch中,然后启动电压泵执行擦写操作。实际嵌入式开发环境中,这种操作会被做成API直接调用。


一个逻辑上存在但是物理上没有存储器的地址。
首先,指针操作访问的都是逻辑地址。实际芯片设计中,存储器的大小是固定且有效的,并不是每个逻辑地址都对应这逻辑单元。此时如果访问一个不存在的地址(无论读写),硬件上一般会触发NMI。


NMI是Non Maskable Interrupt的缩写,即不可屏蔽中断。虽然名字上带有“中断”二字,实际是一种错误,优先级比常见的中断不知高到哪里去了(这个吐槽好熟悉啊,长者教诲不能忘)。这个是越界访问最恐怖的归宿,因为进入NMI之后,系统会进行错误处理,而NMI常用于硬件底层错误的服务(比如RAM的校验错误、协处理器请求、IO通道检查错误),完成NMI服务后上层软件运行成什么样子,也就不得而知了。


当然,访问其他硬件上禁止访问的地址,也会有类似的结果。


所以,题主的问题与答案是:


那么a[11]存在哪里了?——在RAM中。


引申问题:


会引入错误吗?——如果地址虽然越界但仍在RAM中,不会引起大问题,最多是把你不想改写的数据改写了。


每次都会这样吗?——一般都是,但是不排除哪次访问到了不得了的地方导致严重的错误。