《C语言深度剖析》第五章 内存管理 p1(完结)( C语言从入门到入土(进阶篇)

目录

内存管理

1. 什么是动态内存

2. 为什么要有动态内存

3. 栈、堆和静态区

4. 

4.1. 常见的内存错误

4.1. 注意

4.2. 可以用memset来初始化

4.3. 内存泄漏如果程序退出了,问题还在吗?

4.4. 内存开辟实际上比我们预要开辟得多,为什么? 

4.5. 内存释放的本质上是什么?

5. C中动态内存“管理”体现在哪 


本章节文章是作者通过观看《C语言深度剖析》等各种资料总结的精华,基础部分省略了不少,是为了让大家能够更加深入了解C语言的魅力!因为为了避免与之前的文章发生赘述,所以就直接讲作者认为的精华部分哈!现在正文开始! 

 谁都不能阻挡你成为更优秀的人。 

内存管理

1. 什么是动态内存

就是开辟在堆上的内存,而且要用特定的函数去开辟,我们常用的是malloc,和free(释放空间)

返回一个内存块给用户,返回成功就是那块空间的起始地址,失败就是NULL。

free释放空间,参数就是之前获取返回值的指针变量。

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <windows.h>
#define N 10
int main()
{
        int *p = (int*)malloc(sizeof(int)*N); //动态开辟空间
        if (NULL == p){
        return 1;
        }
        for (int i = 0; i < N; i++){
        p[i] = i;
        }
        for (int i = 0; i < N; i++){
        printf("%d ", i);
        }
        printf("n");
        free(p); //开辟完之后,要程序员自主释放
        system("pause");
        return 0;
}
 

2. 为什么要有动态内存

1. 在技术方面,普通的空间申请,都是在全局或者栈区,全局一般不太建议大量使用,而栈空间有限,那么如果一个应用需要大量的内存空间的时候,需要通过申请堆空间来支持基本业务。
 

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <windows.h>
#define N 10
int main()
{
    char a[1024 * 1024]; //就简单的1M空间,程序就崩溃了
    system("pause");
    return 0;
}
2. 在应用方面,程序员很难一次预估好自己总共需要花费多大的空间。想想之前我们定义的所有数组,因为其语法约束,我们必须得明确"指出"空间大小.但是如果用动态内存申请(malloc),因为malloc是函数,而函数就可以传参,也就意味着,我们可以通过具体的情况,对需要的内存大小进行动态计算,进而在传参申请,提供了很大的灵活性

3. 栈、堆和静态区

 

我们发现地址从上到下依次增大,而且堆区(heap addr)和栈区差距巨大,也就是说中间有巨大的漏空(堆区向上增长,栈区向下增长)(也就是我们图中所看到的)。 

 

问一个问题:在C语言中,为何一个临时变量,使用static修饰之后,它的生命周期变成全局的了? 

编译的时候被编译进了全局数据区。(全局特性,但是注意作用域还是不变,只是生命周期是整个程序的生命周期)
PS:栈区随着申请与释放而进行空间管理,而其他的区,基本上都是随着整个程序的运行而一直存在。

4. 

4.1. 常见的内存错误

1.指针没有指向一块合法的内存
2.为指针分配的内存太小
3.内存分配成功,但并未初始化
4.内存越界(越界是一个很严重的问题,但不是所有严重的问题都会表现出来 )
5.内存泄漏

4.1. 注意

assert称为断言,如果内部条件不满足,就会报错,满足就什么都不做。但是一般用if,因为如果我们需要的情况是指针为NULL(默认行为)assert是解决不了的,而且assert一般只有在发布方式为 Debug下面有效,还有要是我们自己定义一个地址去指向非法行为,assert(if)也是没有办法去判断的,因为他也不知道用那个地址是不是非法的,所以我们写指针如果未直接引用,就要设置为NULL。 

4.2. 可以用memset来初始化

4.3. 内存泄漏如果程序退出了,问题还在吗?

答案是当一个程序开辟了很多空间但是没有去释放,发生内存泄漏,但是如果程序结束,操作系统会强制把开辟的内存回收,也就是释放内存,所以是不在了。(注意不是编译器回收哈,因为代码运行起来之后就和编译器没有关系了)

那问题又来了,那什么程序最怕内存泄漏,就是那些不会主动退出的程序或者说很少主动退出的程序。(一般常见的有:操作系统,杀毒软件,服务器程序)(我们把这种经常性得使用的程序称为常驻(内存)进程(程序))

4.4. 内存开辟实际上比我们预要开辟得多,为什么? 

我们可以发现,我们free的时候只知道起始地址,并没有说要释放多少字节 

这是执行free语句的结果

但是我们free的时候发现变红的远不止20个,也就是说释放的远不止20个空间,也就意味着曾经申请的空间一定比20个字节多(这个多(kookie)是个固定的值,但是看编译器等)。多出来的部分其实是记录这次申请的更详细信息(申请空间多大,申请时间等)。再多说一句,所以申请大空间比较好,因为kookie的比值就小,而刚好申请小空间我们用栈,所以刚好互补。 

4.5. 内存释放的本质上是什么?

我们发现释放前和释放后p的值没变。那此时还能访问堆空间吗?

答案是不能,为什么呢?

这里来一个例子,假设你心中有一个女朋友的变量空间,里面开始什么都没有,后来喜欢上了一个人,然后你们在一起了,她的名字就在我们的变量空间里面,但是后来因为某种原因,她不喜欢你了,分手了,这就相当于free,但是她的名字任然在我们之前的空间里面,但是我们不能再去找她玩了,因为我们现在没有关系了,这个free就相当于取消了我们之间的关系,再去找她,就相当于野指针访问了! 

那free会不会将p置为空?

其实是不会的我们发现,为什么呢?因为此时用p再访问已经就是非法访问了,此时对系统没有影响,影响的只是我们自己的程序(分手后走不出来,和前女友没有关系,伤害的是我们自己啊QAQ),所以系统并没有强制设定为空,但是我们自己并不想这样,因为可能会出错(可能走不出来),所以我们free之后常把他设置为NULL(封心封心)。

5. C中动态内存管理体现在哪 

在今天的学习中,我们有效学到的函数是malloc和free,能够进行有效的空间申请和释放了
那么,通常书中所说的内存“管理”体现在哪里呢?难道就是malloc和free?
内存管理的本质其实是:空间什么时候申请,申请多少,什么时候释放,释放多少的问题。
1. 场景:C的内存管理工作是由程序员决定的,而程序员什么时候申请,申请多少,什么时候释放,释放多少都是有场景决定的(比如上面的链表操作),而大部分书中,是讲具体操作,很少有场景,所以管理工作体现的并不直观。不过我们现在能理解即可。
2. 其他高级语言:像java这样的高级语言,语言本身自带了内存管理,所以程序员只管使用即可。换句话说,内存管理工作,程序员是不用关心的。但是C是较为底层的语言,它的内存管理工作是暴露给程序员的,从而给程序员提供了更多的灵活性,不过,管理工作也同时交给了程序员。
所以,因为上面的两点,C中内存章节,基本都叫做内存管理
在C中,程序员+场景=内存管理

今天的内容就到这里了哈!!!

要是认为作者有一点帮助你的话!

就来一个点赞加关注吧!!!当然订阅是更是求之不得!

最后的最后谢谢大家的观看!!!

你们的支持是作者写作的最大动力!!!

下期见哈!!!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>