事情经过
11月3日晚,今天遇到了一个神奇的现象,一个大小为10的数组可以容纳200个数据,直接震惊我了!
今天发11月2日的参考代码,有一个同学给我看他的代码,大概是这样的
int main(){
int a[10];
....
for(int i = 0 ; i < n; i++){
scanf("%d",&a[i]);
}
....
}
问我为什么过不了测试数据,我告诉他把数组开大一点,因为测试数据会有200个数据。
但是他又告诉我,为什么他室友定义的数组大小是10,却可以通过样例,他给我看了室友的代码
int a[10];
int main(){
....
for(int i = 0 ; i< n; i ++){
scanf("%d",&a[i]);
}
...
}
我不信,然后我就拿去洛谷平台试了一下,果然通过测试了!
然后我又在本地的DEV中运行,我丢,200个数据果然通过可以正常运行并输出结果。
通过对比两者的代码,我发现,他室友将数组定义为全局数组,他定义为局部数组,为什么定义为局部数组不能通过测试数据?
我当时想:是不是全局数组在动态运行的过程中,进行了扩容?然后我又在程序的末尾检测了一下数组的容量大小。
printf("%d",sizeof(a)/sizeof(a[0]));//
//输出10
又震惊了我一下,数组的容量是10 ,竟然可以容量200个数据。
然后我又打印a[0]
和a[200]
的地址
printf("%d %d",&a[0],&a[200]);
// 4223040 4223840
// 打印发现两者的地址相差 800
// 也就是200*4,因为一个int占4个字节
数组元素的地址是正确的,但是数组的容量却是小的,我似乎有了一些思路。
解释
通过在网上的搜索,我了解到C/C++是不会对数组的越界做出判断的,也就是说可以对数组进行越界访问和操作
数组在定义时,规定了数组的大小是10,在程序运行的过程中,对数组进行赋值操作,当下标大于等于10以后,此时继续进行存取操作是越界的,但是C/C++没有数组越界的判断,所以可以对数组之外的内存区域进行了操作
那样这就容易解释了为什么容量为10 的数组能够存200个数据。
那么还有一个问题没有解决:同样是数组越界?为什么全局数组可以存取200个数据且得到正常的结果,局部数组就不能呢?
此时得到的运行结果是这样的:数据都是正常的。
此时再换成局部数组,继续同样的操作
得到的结果是这样的
发现打印出了错误的数据。
说明程序对于局部数组的越界是不稳定的操作,但是对于全局数组的越界操作缺失稳定的
至于为什么会这样?那就需要用C语言的不同的内存区域来解释了。
C语言内存管理
11月10日,终于有空来整理我的笔记了。
在C/C++ 的内存中有5个分区,分别是堆区、栈区、全局/静态存储区、常量存储区、代码区。
-
栈区:程序运行时由编译器自动分配,当我们的代码在编译时,就会为局部变量、形参、返回值分配内存,这段内存属于栈区,当程序结束时由编译器自动释放。栈的特点是先进后出,在内存中由高地址向低地址扩展。栈的空间比较小,一般是几M,所以一般数组开的太大,就会有爆栈
-
堆区:堆区是我们自己可以手动分配的区域,平时我们用malloc、calloc、new、free等控制的就是堆区内存,堆区的地址是由低地址向高地址扩展,我们在使用堆内存时,一定要牢记释放不用的内存区域,防止内存泄漏
-
全局区:也叫静态区,这一段内存也是在编译时由编译器分配的,全局变量和静态变量都是存储到这一块区域,程序结束后自动释放
-
常量区:存放常量类型,例如常量字符创
-
代码区:我们写的代码的二进制形式就存储到代码区,一般我们不用关心
通过上面的分析,我们重新回到刚开头的位置,由于局部数组分配到栈区,栈区较小,所以数组越界之后的内容不会稳定,会被其他的变量覆盖掉。
全局数组分配到堆区,但是堆区比较大,数组虽然越界了,但是其他的变量的分配对越界部分的内存影响的概率较小。
总结数组大小根据实际的要求开,题目要求最大数据量是多少,尽量就开到多少,尽量将数组定义为全局数组。无论是全局数组还是局部数组,都不要数组越界,容易产生莫名其妙的错误
标签:10,200,堆区,C语言,越界,内存,数组 From: https://www.cnblogs.com/itkkk/p/16887177.html