C语言数组越界和内存分布

事情经过

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个数据且得到正常的结果,局部数组就不能呢?

image-20221103230428706

此时得到的运行结果是这样的:数据都是正常的。

image-20221103230515292

此时再换成局部数组,继续同样的操作

image-20221103230659793

得到的结果是这样的

image-20221103230640549

发现打印出了错误的数据。

说明程序对于局部数组的越界是不稳定的操作,但是对于全局数组的越界操作缺失稳定的

至于为什么会这样?那就需要用C语言的不同的内存区域来解释了。

C语言内存管理

11月10日,终于有空来整理我的笔记了。

在C/C++ 的内存中有5个分区,分别是堆区、栈区、全局/静态存储区、常量存储区、代码区

20210319215715539

  • 栈区:程序运行时由编译器自动分配,当我们的代码在编译时,就会为局部变量、形参、返回值分配内存,这段内存属于栈区,当程序结束时由编译器自动释放。栈的特点是先进后出,在内存中由高地址向低地址扩展。栈的空间比较小,一般是几M,所以一般数组开的太大,就会有爆栈
  • 堆区:堆区是我们自己可以手动分配的区域,平时我们用malloc、calloc、new、free等控制的就是堆区内存,堆区的地址是由低地址向高地址扩展,我们在使用堆内存时,一定要牢记释放不用的内存区域,防止内存泄漏
  • 全局区:也叫静态区,这一段内存也是在编译时由编译器分配的,全局变量和静态变量都是存储到这一块区域,程序结束后自动释放
  • 常量区:存放常量类型,例如常量字符创
  • 代码区:我们写的代码的二进制形式就存储到代码区,一般我们不用关心

通过上面的分析,我们重新回到刚开头的位置,由于局部数组分配到栈区,栈区较小,所以数组越界之后的内容不会稳定,会被其他的变量覆盖掉。

全局数组分配到堆区,但是堆区比较大,数组虽然越界了,但是其他的变量的分配对越界部分的内存影响的概率较小。

总结数组大小根据实际的要求开,题目要求最大数据量是多少,尽量就开到多少,尽量将数组定义为全局数组。无论是全局数组还是局部数组,都不要数组越界,容易产生莫名其妙的错误

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值