什么叫数组:
数组(Array)是一种线性表数据结构。它是一组连续的内存空间,来存储一组具有相同类型的数据。
什么线性表跟非线性表:
线性表:通俗一点就是数据像一条线一样排成的结构,线性表上的数据只有前后两个方向,另外像链表,队列,栈等也是线性表的数据结构。
非线性表:像二叉树,图,堆等,数据之间不只是前后的关系。
数组是连续的内存空间和相同类型的数据。这让它有着高效地随机访问和低效地删除跟添加数据。为了保证连续性,就需要大量地数据搬移,效率就很差。
高效地数据访问是如何做到的?
比如我们申请了一个长度为10的int型的数组,(说到这儿,数组的长度是指存放线性表的存储空间的长度,线性表的长度指的是线性表中数据元素的个数),那么计算机分配了一块连续的内存,假设1000~1038,内存的首地址1000。那么计算机通过地址来访问内存中的数据,a[i] = 1000 + i*4,4是数组中每个元素的大小。
低效地插入与删除:
插入:为了保证内存数据的连续性,将一个数据插入到第k位置的大小为n的数组中,那么k~n的元素都要顺序地往后移动,最好的时间复杂度为1,最坏为n,平均为O(n)。
改进:如果一个数组储存的元素并不是有序的,只是简单的用来存储元素,那么可以将新的元素放在k的位置,原来的元素放到最后。
删除:删除也需要搬数据,不然内存中出现空洞导致不连续。最好时间复杂度1,最坏为n,平均为n。
数组越界:
int main(int argc, char* argv[])
{
int i = 0;
int arr[3] = {0};
for(; i<=3; i++)
{
arr[i] = 0;
printf("hello world\n");
}
return 0;
}
因为,数组大小为 3,a[0],a[1],a[2],而我们的代码因为书写错误,导致 for 循环的结束条件错写为了 i<=3 而非 i<3,所以当 i=3 时,数组 a[3]访问越界。
我们知道,在 C 语言中,只要不是访问受限的内存,所有的内存空间都是可以自由访问的。根据我们前面讲的数组寻址公式,a[3]也会被定位到某块不属于数组的内存地址上,而这个地址正好是存储变量 i 的内存地址,那么 a[3]=0 就相当于 i=0,所以就会导致代码无限循环。数组越界在 C 语言中是一种未决行为,并没有规定数组访问越界时编译器应该如何处理。
因为,访问数组的本质就是访问一段连续内存,只要数组通过偏移计算得到的内存地址是可用的,那么程序就可能不会报任何错误。
函数体内的局部变量存在栈上,且是连续压栈。
申请的内存地址是从高位到低位的:
int i = 0;
int j = 0;
int y = 0;
int arr[3] = {0};
qDebug() << &i;
qDebug() << &j;
qDebug() << &y;
qDebug() << &arr[0];
qDebug() << &arr[1];
qDebug() << &arr[2];
0x70fdac
0x70fda8
0x70fda4
0x70fd98
0x70fd9c
0x70fda0