6.动态内存申请
6.1 动态分配概述
在数组一章中,介绍过数组的长度是预先定义好的,在整个程序中固定不变,但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定 。
为了解决上述问题,C语言提供了-些内存管理函数,这些内存管理函数可以按需要动态的分配内存空间,也可把不再使用的空间回收再次利用。
**动态分配内存就是在堆区分配空间**
6.2 静态分配、动态分配
静态分配
1、在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式。int a[10]
2、必须事先知道所需空间的大小
3、分配在栈区或全局变量区,一般以数组的形式
4、按计划分配
动态分配
1、在程序运行过程中,根据需要大小自由分配所需空间
2、按需分配
3、分配在堆区,一般使用特定的函数进行分配
6.3 动态分配函数
-
malloc函数
#include <stdlib.h> void *malloc(unsigned int size); // 功能:在堆区开辟指定长度的空间 // 参数: // size:要开辟的空间的大小返回值: // 成功:开辟好的空间的首地址 // 失败:NULL
注意
1. 在调用 malloc之后,一定要判断一下,是否申请内存成功
2. 如果多次 malloc,申请的内存,第1次和第2次申请的内存不一定是连续的
3. 使用malloc开辟空间需要保存开辟好的空间首地址,但是不确定空间的用途,所以本身的返回值是void *
,使用的时候需要根据实际情况进行强制类型转换#include <stdio.h> #include <stdlib.h> char *func(){ //char ch[100]= "hello world"; //静态全局区的空间只要开辟好,除非程序结束,否则不会释放,所以//如果是临时使用,不建议使用静态全局区的空间 //static char ch[100]= "hello world"; //堆区开辟空间,手动申请手动释放,更加灵活 char *str =malloc(100 *sizeof(char)); str[0] = 'h'; str[1] = '\0'; return str; } int main(int argc, char const *argv[]) { char *p; p = func(); printf("p = %s\n",p); return 0; }
输出结果
p = h
-
free函数(释放内存函数)
头文件:#include <stdlib.h> 函数定义:void free(void *ptr) 功能:只能释放堆区的空间 参数:ptr ,开辟后使用完毕的堆区空间首地址 返回值:无
#include <stdio.h> #include <stdlib.h> char *func(){ //char ch[100]= "hello world"; //静态全局区的空间只要开辟好,除非程序结束,否则不会释放,所以//如果是临时使用,不建议使用静态全局区的空间 //static char ch[100]= "hello world"; //堆区开辟空间,手动申请手动释放,更加灵活 char *str =malloc(100 *sizeof(char)); str[0] = 'h'; str[1] = '\0'; return str; } int main() { char *p; p = func(); printf("p = %s\n",p); // 使用free释放空间 free(p); // 防止野指针 p = NULL; return 0; }
注意:
- free函数只能释放堆区的空间,其他区域的空间无法使用free
- free释放空间必须释放malloc或者calloc或者reloc的返回值对应的空间,不能说只释放一部分free(p)
- 当free后,因为没有给p赋值,所以p还是指向原先动态申请的内存。但是内存已经不能再用了,p变成野指针了,所以一般为了放置野指针,会free完毕之后对p赋为NULL
- 一块动态申请的内存只能free一次,不能多次free
-
calloc函数
头文件:#include <stdlib.h> 函数定义:void *calloc(size_t nmemb,size_t size) 功能:在堆区申请制定大小的空间 参数: nmemb:要申请的空间块数 size:每块的字节数 返回值: 成功:申请空间的首地址 失败:NULL
注意:
malloc 和 calloc函数都是用来申请内存的。
区别:- 函数的名字不一样
- 参数的个数不一样
- malloc 申请的内存,内存中存放的内容是随机的,不确定的,而 *calloc函数申请的内存中的内容为0
// 在堆中申请了3块,每块大小为 100个字节,即 300个字节连续的区域 char *p=(char *)calloc(3,100);
-
realloc函数(重新申请内存)
在调用
malloc
和calloc
函数单次申请的内存是连续的,两次申请的两块内存不一定连续。有些时候有这种需求:先用 malloc 或者 calloc,申请了一块内存,还想在原先内存的基础上挨着申请内存。或者开始时候使用 malloc 或 callo 申请了一块内存,再释放后边的一部分内存。
为了解决这个问题,就发明了realloc这个函数。
头文件 #include<stdlib.h>
函数的定义: void* realloc(void *s,unsigned int newsize);
参数:
s - 原本开辟好的空间首地址
newsize - 重新开辟空间的大小
返回值:
新的空间的首地址
函数的功能: 在原先s指向的内存基础上重新申请内存,新的内存的大小为 new size 个字节
1. 如果原先内存后面有足够大的空间,就追加;
2. 如果后边的内存不够用,则relloc函数会在堆区找一个 newsize个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。
char *p;
p = (char *)malloc(100);
// 在100个字节后面追加50个字节
p = (char *)realloc(p,150);
// 将100个字节减少到50个字节
p = (char *)realloc(p,50);
注意:malloc calloc relloc 动态申请的内存,只有在fee或程序结束的时候才释放。
6.4 内存泄露
内存泄露的概念: 申请的内存,首地址丢了,找不了,再也没法使用了,也没法释放了,这块内存就被泄露了。
案例1
int main(){
char *p;
p=(char *)malloc(100);
//接下来,可以用p指向的内存了
p="hello world";//p指向别的地方了
//从此以后,再也找不到你申请的100个字节了,则动态申请的100个字节就被泄露了
return 0;
}
案例2
void fun(){
char *p;
p=(char *)malloc(100);//接下来,可以用p指向的内存了
}
// 不断地申请空间,导致内存炸裂
int main() {
fun();
// 后面每次调用一次fun泄露100个字节
fun();
fun();
...
return 0;
}
解决方式:
- 申请的空间使用完毕后,使用free释放空间,指针指向
NULL
- 可以将内存空间地址返回,然后释放,指针指向
NULL