C语言中的内存动态分配
一.前言
在 c 语言中,静态类型一般都存储在栈区,而动态内存一般都在堆区,在之前的学习中,我们知道在c语言中如果要创建一个可变的数组,当然,你可以使用 C99 标准中的新的规范,允许你使用变量动态的创建数组大小,但是在这之前,你只能使用宏定义的方式,还有我们今天要说的东西 --- 动态内存申请;
二.malloc
在使用之前,我们需要先将 malloc
所在的包引入。
#include<stdlib.h>
下面就是一个简单的例子申请一个动态内存。
int *p = (int *)malloc(sizeof(int));
以上的代码代表着申请一个大小为 sizeof(int)
大小的内存,并将内存的首地址返回,我们使用 p
指针进行接收,因为 malloc
返回值是一个 void *
指针,但是我们的 p
指针类型是 int *
,所以我们这里需要强制类型转换。
1.创建一个一维数组
int *p = (int *)malloc(sizeof(int)*10);
内存申请好后,这个时候如果我们去输出值,那么你将会得到一串乱码,因为这个时候你还没有做初始化,当然你可以使用等会我们要说的 calloc
函数,他会将内存中的值全部初始化为 0 ;
但是,在此之前,我们也可以使用 memset
函数用来将内存中值初始化。
memset(p,0,sizeof(int)*10);
// 注意一般不这么做,memset常用于对字符数组进行初始化。
顺便再说一个类似的函数 memcpy
.它可以将一个内存中的值拷贝到另一段内存中。
int arr[10] = {0};
memcpy(p,arr,sizeof(int)*10};
创建完后,我们可以使用如下的方式输出内存中的值,和数组操作类似。
1.使用下标
for (int i = 0;i<10;i++){
printf("%d ",p[i]);
}
2.使用指针
for (int i = 0;i<10;i++){
printf("%d ",*(p+i));
}
2.创建一个二维数组
我们知道,二维数组是其实就是由多个一维数组构成的,那么我们就可以开始实现一下。
1.指针数组
int ** p= (int **)malloc(sizeof(int *)*4);
// 为每个子数组生成空间,也就是二维数组中的列数
for(int i= 0;i<4;i++){
*(p+i) = (int *)malloc(sizeof(int)*4);
if (*(p+i) == NULL){
printf("申请内存失败\n");
}
}
以上就是二维数组声明的过程,如果要使用的话,就和正常的二维数组一样。
for (int i = 0;i<4;i++){
for(int j = 0;j<4;j++){
printf("p[%d][%d] ",i,j,p[i][j]);
}
printf("\n");
}
但是,特别要注意,在这里释放内存空间的时候要注意,需要逐个释放每个一维数组的内存,然后再释放指针数组的内存,如下:
for(int i= 0;i<4;i++){
free(p[i]);
}
free(p);
当然我们这里其实做的复杂了,我们可以使用如下的方式创建二维数组;
int *p[4] = {};
// 其余的代码不变
在最后的销毁内存的时候,我们可以这样理解其实,int ** p= (int **)malloc(sizeof(int *)*4);
和 int *p[4] = {};
其实是没有区别的,只不过第二种更加简单,因为c语言自动帮助我们实现了申请内存的和释放内存,所以,在销毁内存的时候少了最一步的 free(p)
,如下。
for(int i= 0;i<4;i++){
free(p[i]);
}
2.数组指针
除了第一种方式,我们还可以使用数组指针的方式。
// 一次性把所有的内存申请完
int (*p)[4] = (int (*)[4])malloc(sizeof(int)*4*4);
同样,它也不够灵活,因为创建的二维数组列数是固定的。其余的使用方式和上面的指针数组一样,只有最后一步销毁内存不一样,因为我们使用的是指向数组的指针,所以仅仅只需要 free(p)
即可。
三.calloc
与上述的 malloc
的功能基本一致,只是多了一个将申请到的内存中的值全部赋值为一,也就是初始化。
int *p = (int *)calloc(0,sizeof(int)*4);
for (int i = 0;i<4;i++){
printf("%d ",p[i]);
}
控制台输出:
0 0 0 0
四.realloc
这个函数主要为解决在申请的空间不够用的时候,我们可以使用它来扩容,如下是它的函数原型。
void *realloc(void *ptr, size_t size)
我们注意到,他会返回一个万能指针,这是因为,我们通过它扩容结束后,可能会得到一个新的地址,这是因为可鞥它在申请与之前的地址连续的地址的发现内存不够了,于是只能去找一块新的连续空间,所以,我们一般都会将原来的指针指向新获得到的地址。
五.free
释放内存的,注意不能重复释放。