C语言内存管理总结

前言

C语言的魅力莫过于其内存管理机制,给了开发者更多自由的空间,用好了可以节省内存资源,控制成本。然而用不好却会后患无穷。下面我们从系统内存的分配创建出发,来分析实例操作中内存管理不当造成的后果

内存

在计算机中,每个进程之间的内存相互独立,这里先卖个关子,先不讨论操作系统怎么给进程分配内存,因为涉及到了地址映射等知识,较为复杂。现在我们只需要知道,在通常情况下,进程之间的内存是不能互相访问的,注意是正常情况下。例如你打开一个QQ,同时在看快播,那么它们的内存自然不能互相访问。

每当运行程序(打开一个进程)时,操作系统会为这个进程独立开辟一段内存,
如图所示:
在这里插入图片描述

一、 进程内存空间的组成

在进程开辟内存时,会被分为四个区域,分别是栈空间,堆空间,数据空间,代码空间。

栈空间:用来存储一些临时变量。比如:局部变量、返回值、地址、函数参数…这块空间不宜分配大量数据操作,否则会导致栈溢出。

堆空间:相比于刚刚的栈空间,堆空间可以分配的内存往往是大块的。主要用于动态分配内存(malloc…),在进程运行的过程中由开发者自己进行分配与释放。在进程结束后,操作系统会自动给你释放,但是在运行过程中不会自动释放,所以如果忘了释放,短时间可能看不出问题,长时间可能会导致内存泄漏等问题。

数据空间:主要存放全局变量,常量,静态变量。数据空间也可以分为如下两种
(1)全局区:存放全局变量
(2)静态区:存放静态变量

代码空间:用来存放可执行代码,这块区域具有只读属性,其余的没什么好说的。
在这里插入图片描述

二、 栈空间机制理解

说再多,不如实操,下面我们来实战理解一下栈空间内存分配机制

首先我在这里先做一个说明,在有栈溢出保护机制的情况下,char类型会先定义,int类型会后定义。然后栈空间的申请顺序与代码中变量定义顺序相反(后定义的先入栈)。

代码:

#include <stdio.h>

int main()
{
        int  a = 0;
        int  b = 0;
        char c ='0';

        printf("Addr:a=%p,type=int\n", &a);
        printf("Addr:b=%p,type=int\n", &b);
        printf("Addr:c=%p,type=char\n", &c);
    
        return 0;

}

运行结果

Addr:a=0x7ffddffcd070,type=int
Addr:b=0x7ffddffcd074,type=int
Addr:c=0x7ffddffcd06f,type=char

分析一下这个程序,刚刚说了,编译器带有栈溢出保护机制,所以char c类型先定义,然后定义int a类型,然后定义int b类型,再根据这套保护机制中先定义后入栈的机制,那么入栈顺序应该是b,a,c。

如图所示
在这里插入图片描述
首先栈顶代表着最小地址,从上往下地址依次增大。因为b先入栈,所以它在栈的底部,所以它的地址是最大的,其次是a,b。

三、 静态空间机制理解

在刚刚程序的基础上,加上静态变量,我们来观察一下

#include <stdio.h>

int main()
{
        int  a = 0;
        int  b = 0;
        char c ='0';
        static int d = 0;

        printf("Addr:a=%p,type=int\n", &a);
        printf("Addr:b=%p,type=int\n", &b);
        printf("Addr:c=%p,type=char\n", &c);
        printf("Addr:d=%p,type=static int\n", &d);
    
        return 0;

}

运行结果

Addr:a=0x7ffd36bb6d60,type=int
Addr:b=0x7ffd36bb6d64,type=int
Addr:c=0x7ffd36bb6d5f,type=char
Addr:d=0x55dcf3eb2014,type=static int

根据地址可以看出,静态变量d和a,b,c的地址不存在连续,所以它们的内存地址是分开的。

再定义几个变量看看

#include <stdio.h>

int f = 0;

int main()
{
        int  a = 0;
        int  b = 0;
        char c ='0';
        static int d = 0;
        static int e = 0;

        printf("Addr:a=%p,type=int\n", &a);
        printf("Addr:b=%p,type=int\n", &b);
        printf("Addr:c=%p,type=char\n", &c);
        printf("Addr:d=%p,type=static int\n", &d);
        printf("Addr:e=%p,type=static int\n", &e);
        printf("Addr:f=%p,type=global int\n", &f);
    
        return 0;

}

运行结果

Addr:a=0x7ffc7bfca770,type=int
Addr:b=0x7ffc7bfca774,type=int
Addr:c=0x7ffc7bfca76f,type=char
Addr:d=0x563bc71a0018,type=static int
Addr:e=0x563bc71a001c,type=static int
Addr:f=0x563bc71a0014,type=global int

从以上运行结果中证实了上述内容的真实性,并且也得到了一个知识点,栈空间、数据空间都是使用栈结构对数据进行存储。

四、 栈空间大小的有限性

刚刚说过,大内存会有栈溢出的可能,所以我们玩一把试试

#include <stdio.h>

int main()
{
        int  a[1024*1000000];

        printf("Addr:a=%p,type=int\n", &a);
    
        return 0;

}

运行结果

Segmentation fault (core dumped)

发现程序无法正常运行,直接栈溢出退出程序。

当需要使用大片内存的时候,建议使用malloc

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值