目录
本章节文章是作者通过观看《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中,程序员+场景=内存管理
今天的内容就到这里了哈!!!
要是认为作者有一点帮助你的话!
就来一个点赞加关注吧!!!当然订阅是更是求之不得!
最后的最后谢谢大家的观看!!!
你们的支持是作者写作的最大动力!!!
下期见哈!!!