嵌入式开发基础从启动例程到硬件之array[-1]为什么会动作(数组实体)
array[-1]为什么会动作
概述
编写程序时,没有比数组更容易使用的了。因此,不知什么时候,“数组”这个抽象的概念在脑海中形成了,你有没有想过什么特别的东西呢?
在此,我们将探讨C语言中的数组实体。
回想起内存空间的样子
使用数组时需要注意的是,例如,
int array[10];
如果声明,array[0],array[1],.,array[9]是“索引使用0到9”。在这个范围内使用索引是一种彬彬有礼的程序的制作方法。但是,即使使用超出范围的索引,程序也会运行(除了作为动作是否正确之外)。极端的话,在array[-1]中也会动作。你能解释为什么吗?我不能只用数组这个概念来解释这一点。考虑数组的实体。
首先,让我们回忆内存空间的样子。所谓内存空间,是为了配置程序的代码和数据的(逻辑性的)场所,实际上存在于微型计算机的内置内存和微型计算机外部的RAM和ROM中。MMU(Memory Management Unit)的微型计算机也有,
在这里,为了简化说明,假设没有MMU。在内存空间中分配了地址,由此可以唯一地指定哪个内存。内存空间的地址,例如如果使用32位来表示的话,可以从0x00000000地址分配到0xffffffff地址,
在实际使用时,并不是所有这些地址都可以使用。只有安装了与地址对应的内存的部分才能使用。例如,在图3-1中,显示了从0x00000000地址到0x0000ffff地址和0xffffff0000地址到0xffffffff地址可以使用。
那么,数组array是如何在这个内存空间中存在的呢?
在图3-1中,数组数组排列在从0x1020到0x1047。由此,数组[0]是0x1020,array[1]是0x1024,array[2]是0x1028,以此类推。这些地址不是分配给数组中的每个元素的地址,而是根据数组的开头地址和偏移量计算的。
也就是说,在数组[5]的情况下,
数组[5]的地址=数组数组的起始地址+第五个元素的偏移。
=0x1020+5×sizeof(Int)。
=0x1020+0x14=0x1034(3-1)。
也就是说,只要确定数组的开头地址,就可以从从索引和数组的类型中知道的数据长度(在这个例子中是sizeof(Int))知道数组的各要素的地址。也就是说,为了利用数组,只要有内存空间上的连续场所就可以了。
在这里所示的内存空间中,灰色的部分表示实际安装了内存的地址(实际使用的内存部分)。排列的实体只不过是贴在内存空间上的某个领域。
当你想到指针时,你会看到一个数组
接下来,我将解释数组和指针之间的关系,因为如果使用前面的例子,array表示数组的开头地址,
array = 0x1020。
另外,各数组要素的地址,使用&运算符(计算指定数据的地址),可以表示如下。
&array[0]=0x1020。
&array[1]=0x1024。
&array[2]=0x1028。
&array[3]=0x102c。
&array[4]=0x1030。
&array[5]=0x1034。
这些地址的计算与刚才公式(3-1)的计算相同。用于存储这些地址信息的是指针。指针不仅是地址信息(如果只是这样的话作为整数数据处理就足够了),将数据写入该地址,
或者也可以用来读取存储在该地址中的数据。为此需要类型信息,所以指针也同时具有类型信息。也就是说,
int *p = array;
这样记述的话,p是指示数组array开头的指针,该指针表示的地址的内容是int型。从嵌入式软件来看,数组是粘贴在内存上的某个区域,并不是特别的。如上所述的地址计算都是由C编译器进行的,所以数组也可以说是C编译器创造出的假象。也就是说,数组的实体是,具有与数组相同的类型信息,可以认为是指示数组开头地址的指针。
顺便问一下,你知道(int*)0x1020这个表达的意思吗?
0x1020是整数,如果把它看作地址的话,可以解释为,在(int*)的传送中带有类型信息的指针(即,在地址0x1020中其内容是int的指针)。
int *p = (int *)0x1020; (3-2)
即
int *p = array;
在普通的C语言的程序中不使用式(3-2)那样的表现,但是在嵌入式程序中为了访问周边I/O的寄存器操作在日常中使用,所以请一定要记住。
那么,考虑array[-1]。和公式(3-1)一样考虑的话,如下所示。
数组[-1]的地址=数组array的起始地址+(-1)个元素的偏移。
=0x1020-1×sizeof(Int)。
=0x1020-0x14。
=0x101c。
C编译器机械地进行地址计算。也就是说,array[-1]指向0x101c地址的int数据(根据图3-1,array[-1]的值为“256”)。对于程序的动作,啊?,数组的索引可能是负的。
数组和指针的区分使用
数组的实体是指针。因此,在写程序的时候,无论是使用数组还是使用指针,在原理上都是一样的。在现实中,从程序的可读性和扩张性,或者效率方面分别使用两者。例如,虽然依存于C编译器,根据使用数组还是使用指针,C编译器生成的代码有时会出现效率方面的差异。也就是说,
int array[10], n;
for(n=0; n<10; ++n) {
array[n] = 0;
}
和
int array[10], n, *p;
for(n=0, p=array; n<10; ++n) {
*p++ = 0;
}
如果您有兴趣,请将汇编源输出到C编译器的选项进行比较,这是有趣的。
结束语
理解数组的实体,即数组和指针的关系,在嵌入式软件中是极其重要的。另外,包括负的索引的情况,访问超过数组领域的场所的数据发生的错误意外的多。
上一篇: 【Qt界面开发项目(2)】定时计数器
下一篇: 【LiteOS】03-基于模板创建工程