函数原型及说明
void *malloc(long NumBytes)
该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。
关于分配失败的原因,应该有多种,比如说空间不足就是一种。
void free(void *FirstByte)
该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
注意点
free的一些相关内容参考:
【C语言】free()函数详解(动态内存释放函数)_free函数-CSDN博客
c语言的free函数与内存空间释放_链表释放内存free函数-CSDN博客
- 申请了内存空间后,必须检查是否分配成功。
- 当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL(一般全局变量需要,局部变量时不太需要),防止程序后面不小心使用了它。
- 在malloc()和free()之间,不能对其指针表示的地址进行修改。
- free释放的是由动态分配内存函数分配的内存空间,而不是指针。所谓释放,其实就是解除了申请的堆内存空间的占用,申请之后释放之前,该空间不能再其他变量使用,释放之后,对应内存空间可以被其他变量所占用。free后,指针仍然指向分配的内存空间,指针指向的地址没有变化,地址内的内容也没有发生变化。变化的是这块内存的可使用权限。
- 释放只能释放一次,如果一块空间释放两次或者两次以上会出现错误(但是释放空指针例外,释放空指针也等于什么也没做,所以释放多少次都是可以的。)
- malloc只管分配内存,并不能对其进行初始化,所以得到的一片新内存中,其值将是随机的。calloc() 函数将分配的内存全部初始化为零。这也是malloc 和 calloc 之间的一个不同点。
- malloc分配的内存空间在逻辑上是连续的,而在物理上可以不连续。原理可参考:内存分配不再神秘:深入剖析malloc函数实现原理与机制-阿里云开发者社区 (aliyun.com)
一些QA
Q:有malloc 一定有free吗?
A:是的
但并不是说一个malloc一定要对应一个free.要看具体的场景。
一旦free,就丧失了指针对这段内存空间的占有权和使用权。
比如物理结构是链表的实现场景,链表中的各个节点存储在当时分配的内存空间中。如果在构造链表的过程中,边申请存储节点的空间边释放节点空间,那么已经建好的结点的内容有可能被抹掉,所以只要有对该链表及其元素的操作,就不能free,一般只有在删除节点时才用free()
另外,比如在创建线程时,传递的参数使用了malloc来分配空间,那么,就只有创建线程出错时才释放空间,否则,整个程序运行过程中,该空间都不会被free.
Q:该free的地方没有free,会发生什么?
首先,什么时候该free?ptr对应的内存空间不再需要被访问或者被修改,那么,必须free.
否则,ptr对应的内存空间将不能被其他变量使用,造成内存泄漏(Memory Leak)
问:为什么要动态分配内存
答1:因为内存太宝贵。
答2:如果全部是静止内存不能释放,对于小的程序可以运行完毕。但是对于大的程序,还没运行完,栈内存就要被占用完,此时就要发生内存泄露。
答3:给定一个占用内存可变大小的变量(假设是数组的长度len),给该变量通过函数动态分配内存后,分配内存的大小是根据数组的长度len决定的,假定用户输入len的大小是5,系统就会动态的给该数组分配长度为5的内存,该段代码运行结束后,系统调用free()函数释放分配的内存,然后接着运行剩下的程序。换句话说,动态分配内存可以根据需要去申请内存,用完后就还回去,让需要的程序用。
问:什么时候需要动态分配内存
答1:当程序中有比较大的数据块需要使用内存的时候使用。原因:比较大的数据块如果使用了静态内存,在该数据块运行完毕后不能动态的释放该内存,直到整个程序运行完才能释放,如果整个程序比较大,有可能因为内存不够而发生错误。
答2:在无法预计内存用量或者是内存的占用可能随着程序运行变化很大的时候就要使用动态分配。比如可能存在程序的内存需求只能在运行时确定的情况。 例如,当需要的内存取决于用户输入。 在这些情况下,程序需要动态分配内存。
总结来说基本就是两种情况:
1. 无法预先确定要空间大小; 2. 要分配的空间太大可能超过栈的大小。
问:难道不可以在该静态分配的内存使用完后,使用free()函数释放吗?
答:不可以,首先malloc() 函数和free()函数必须是配套使用,其次free()函数不能释放普通变量,只能释放指针。
关于free如何知道要释放多少内存空间长度的问题
参考:关于free如何知道要释放多少内存空间长度的问题 - hezhixiong - 博客园 (cnblogs.com)
malloc()申请内存时需要指定申请多大的内存空间,为什么free()释放内存时只需要传递一个指针而不需要指定释放多大的内存空间。
系统在分配内存时除了分配指定的内存空间外,还有分配用于保存内存空间大小等信息。所以内存释放时不再需要再指定释放多大的内存空间,只需要指定该块内存空间的首地址即可。
实战总结
全局变量在程序的整个生命周期内都能访问;
局部变量在函数未出栈之前,都能使用,函数调用结束出了栈,就没法访问了;
而malloc申请的堆内存似乎是介于二者之间,malloc必须在函数体内执行,不能放在函数体外,然后返回一个指向某块内存空间的指针,此时,我们可以使用一个全局变量来接收这个指针,也可以使用一个局部变量来接收这个指针。如果使用全局变量来接收这个指针,那么,我们就能在任意地方操作对应的内存空间,不过,全局变量访问权限太大了,容易被误操作。所以,我们实际中一般就在函数里申请,然后用一个局部变量来接收申请到的空间的指针,然后就在函数里面使用,使用完了,就在函数的最后释放该空间。这是最常规的使用方式。
在某些场景,我们可以把这个指针传递给其他函数,比如通过传参传递给其他函数,或者通过返回值返回给调用者(这里注意和普通局部变量的区别,普通局部变量在返回时就没法使用了,所以不能返回一个局部变量的指针,但是,我们可以返回由malloc申请出来的局部变量的指针),其实就是因为堆内存的特点,只要指向堆内存的指针不丢失,就能被访问被操作,比如A函数调用B函数,然后B函数里malloc了一块堆内存,可以返回堆内存的指针给B函数,然后在B函数里free.
因此,堆内存只要没有被free,就可以通过参数传递在任意地方被访问。具有全局变量的灵活性,又兼具局部变量的安全性。
补充
C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
下面是 realloc() 函数的声明。
void *realloc(void *ptr, size_t size)
ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。