数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具备相同类型的数据。算法
咱们拿一个长度为10的int类型的数组 int[]a=new int[10] 来举例。数组
在我画的这个图中,计算机给数组a[10],分配了一块连续内存空间1000~1039,其中内存块的首地址为base_address = 1000网络
计算机会给每一个内存单元分配一个地址,计算机经过地址来访问内存中的数据。当计算机须要随机访问数组中的某个元素时,它会首先经过下面的寻址公式,计算出该元素存储的内存地址:数据结构
ss = base_address + i * data_type_size
其中data_type_size表示数组中每一个元素的大小。(int类型的大小为4个字节)框架
跟插入数据相似,若是咱们要删除第k个位置的数据,为了内存的连续性,也须要搬移数据,否则中间就会出现空洞,内存就不连续了。数据结构和算法
实际上,在某些特殊场景下,咱们并不必定非得追求数组中数据的连续性。若是咱们将屡次删除操做集中在一块儿执行,删除的效率是否是会提升不少。性能
例如,数组a[10]中存储了8个元素:a,b,c,d,e,f,g,h。如今,咱们要依次删除a,b,c三个元素。为了不d,e,f,g,h这几个数据会被搬移3次,咱们能够先记录下已经删除的数据。每次的删除操做并非真正搬移数据,只是记录数据已经被删除了。当数组没有更多的空间存储数据时,这样就大大减小了删除操做致使的数据搬移。学习
若是你了解JVM,你会发现,这不就是JVM标记清除垃圾回收算法的核心思想吗?没错,数据结构和算法的魅力就在于此,不少时候咱们并非要去死记硬背某个数据结构或者算法,而是要学习他背后的思想和处理技巧,这些东西才是最有价值的。优化
首先分析一下这段C语言代码的运行结果:spa
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; }
你发现问题了吗?这段代码的运行结果并不是打印三行“hello world”,而是会无限打印“hello world”。
由于,数组大小为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语言中是一种未决行为,并无规定数组访问越界时编译器应该如何处理。由于,访问数组的本质就是访问一段连续内存,只要数组经过偏移量计算获得的内存地址可用,那么程序就可能不会报任何错误。
这种状况下,通常都会出现莫名其妙的逻辑错误,就像咱们刚刚举的例子,debug的难度很是大。并且,不少计算机病毒也正是利用了代码中的数组越界能够访问非法地址的漏洞,来攻击系统,因此写代码的时候必定要警戒数组越界。
对于业务开发,直接使用容器就足够了,省时省力。毕竟损耗一丢丢性能,彻底不会影响到系统总体的性能。但若是你是作一些很是底层的开发,好比开发网络框架,性能的优化须要作到极致,这个时候数组就会优于容器,成为首选。