C语言中的内存动态分配

2024 年 6 月 19 日 星期三(已编辑)
/ ,
28
1
摘要
在 c 语言中,动态内存申请是一种重要的操作,它可以让程序在运行时动态地分配内存空间。常用的动态内存申请函数有 malloc、calloc、realloc 和 free。这些函数可以用来申请内存空间、初始化内存、扩容内存以及释放内存。动态内存申请在一维数组和二维数组的创建中得到应用,同时需要谨慎使用,避免内存泄漏和重复释放。
这篇文章上次修改于 2024 年 9 月 10 日 星期二,可能部分内容已经不适用,如有疑问可询问作者。

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

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

释放内存的,注意不能重复释放。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...