关于数组越界却不会报错
数组越界是不一定报错的,系统对越界的检查是设岗检查。
一 ,在进行顺序表的学习时遇到的问题,下面是代码,大家可以直接去看结论。
void TestSeqList1() { SL s1; SLInit(&s1); /*SLPushBack(&s1, 1); SLPushBack(&s1, 2); SLPushBack(&s1, 3); SLPushBack(&s1, 4); SLPushBack(&s1, 5); SLPrint(&s1);*/ SLPushFront(&s1, 1); SLPushFront(&s1, 2); SLPushFront(&s1, 3); SLPushFront(&s1, 4); SLPushFront(&s1, 5); SLPrint(&s1); SLPopBack(&s1); SLPopBack(&s1); SLPopBack(&s1); SLPopBack(&s1); SLPopBack(&s1); SLPopBack(&s1); SLPushFront(&s1, 4); SLPushFront(&s1, 5); SLPrint(&s1); }
上图中头插法插入了五个数据 :5 4 3 2 1
下面尾删法删除了六个数据。为什么能删除六个数据呢?看下图尾删法的代码:
void SLPopBack(SL* ps) { //assert(ps->size > 0);//防止数组越界,如果数组一越界就会弹出错误; ps->size--;//每次数据的大小减小,指针就不会指向被删除的那块内存了; }
因为每次都会将数据大小减一个,所以导致第六次尾删时会使得 ps -> size = -1.
然后添加两个数据,会发现第一次添加的数据没有被输出,只输出了第二次添加的数据,原因就是 ps -> size = -1 ;第一个添加进-1里面,所以没有被输出。
typedef int SLDataType; typedef struct seqList { SLDataType* a; //因为数组大小固定,为了得到一个动态数组,可以抛弃数组,选择指针。 SLDataType size;//定义顺序表中的数据的数量。 SLDataType capacity; //顺序表的容量大小(也就是空间的大小,如何扩容之后会讲)。 }SL;
//头插法 void SLPushFront(SL* ps, SLDataType x) { SLCheckCapacity(ps); for (int i = ps->size - 1; i >= 0; i--) { ps->a[i + 1] = ps->a[i]; } ps->a[0] = x; ps->size++; }
二,结论
根据代码可以有一个疑问,为什么数组下标为-1(可以从头插法代码中可以看到,ps -> size 决定了数组的下标,但是运行没有出错)。
这是C语言非常重视运行时的效率,检查数据越界,编译器就必须在生成的目标代码中加入额外的代码用于程序运行时检测下标是否越界,这就会导致程序的运行速度下降,且不同的编译器不一样,有些编译器会对越界的检查是设岗检查。例如:int a[5] ;那么就会在a[5],a[6]这些地方严格检查,越界就会报错,但是a[10],a[11] .这些地方就可能不会报错,这就像查酒驾一样,在车流量大的道路口严格检查,在没有车流的道路宽松一点,但这不意味着我们可以在车流量少的路上酒驾(说不定那天就被抓了)。也就是即使数组越界不报错,但是我们也要避免数组越界。
- 若是越界的那块地址本来就没有数据占用,就会开辟那块空间用来存储越界的数据(数组开辟的是一串的连续空间)。
- 若是越界的那块地址被数据占用,越界的数组就会覆盖那片地址,将会产生严重的数据丢失。
- C语言允许数组越界是为了提高效率,但是我们要避免这种事情的发生若是担心越界可以使用assert()断言函数。
assert()断言函数