【动态内存管理】动态内存函数

简介: 【动态内存管理】动态内存函数

前言

在使用c语言时,我们经常需要创建一个(些)空间来存放数据,常见的开辟空间方式有2种,即创建一个变量来存放数据 int a = 10; 或是创建一个数组来存放数据int arr[10]; ,但这两种方式在开辟空间时也有一定的局限性,只要一创建,这个空间的大小就已经被指定,不能再根据需要改变其大小。

同时,在计算机中内存是十分宝贵的,若全是静态内存的话,运行较小的程序倒是没什么问题,但若是运行较大的程序时就可能出现程序还没运行完,内存就已经被全部占用完了,所以,为了避免这类问题的发生,我们可以进行动态分配内存。


动态内存函数

动态内存函数,顾名思义,就是来管理动态内存的函数

这些函数包括

开辟动态内存的函数(malloc,calloc),

修改所开辟的动态内存大小的函数(realloc),

释放动态内存的函数(free)。


malloc函数

当需要开辟一块动态内存的时候,就可以使用malloc函数

由图可知,malloc函数的功能为,向内存申请空间,若是申请成功则返回空间开头位置的地址;且所开辟的空间不进行初始化 - 保留初始的不确定的值。

该函数的参数为 size_t size ,即需要开辟空间的大小,单位为字节

该函数的返回类型为void* ,确保所开辟的空间在使用过程中没有类型的限制

若是空间开辟失败则返回一个空指针NULL。

既然返回的是void*类型的指针,该如何运用呢?

int*p = (int*)malloc(20);

若是利用malloc函数来开辟一块空间,在使用时需要将它强制类型转换为需要使用的类型并赋给相应类型的指针

同时,为了避免开辟动态空间失败导致的对空指针NULL非法解引用,应在开辟之后对开辟的空间进行判断,判断其是否为空指针;

正确的使用方法

int main()
{
  int* p = (int*)malloc(20);
  if (p == NULL) {
    return 1;
  }
  return 0;
}

开辟动态空间后使用对应类型的指针对返回的地址进行接收,再将接收的地址进行判断,若是为空指针即开辟空间失败,提前退出程序

也可以使用strerror函数将错误信息打印出来


动态内存的使用

动态空间开辟后可以对该空间进行使用,例如同上代码,现在需要在开辟的空间内存放1-5五个整形。

int main()
{
  //创建
  int* p = (int*)malloc(20);
  if (p == NULL) {
    return 1;
  }
  //使用
  for (int i = 0; i < 5; i++) {
    *(p + i) = i + 1;
  }
  for (int i = 0; i < 5; i++) {
    printf("%d ", p[i]);
  }
  return 0;
}


free函数

在一块开辟的动态空间,我们可以对这块空间进行使用,当然,若是这块空间已经用完的话,应将这块空间及时的进行释放

若是开辟并已经使用完的内存并没有对其及时释放,这块内存则会一直被占用,直至程序运行结束

int main()
{
  //创建
  int* p = (int*)malloc(20);
  if (p == NULL) {
    return 1;
  }
  //使用
  for (int i = 0; i < 5; i++) {
    *(p + i) = i + 1;
  }
  for (int i = 0; i < 5; i++) {
    printf("%d ", *(p + i));
  }
  //释放
  free(p);
  //对指针进行置空
  p = NULL;
  return 0;
}

同时,为了避免指针所指向的空间被提前释放而造成的野指针,应及时将指针置为空指针


calloc函数

calloc函数与malloc函数类似,都是向内存申请一块空间,但较为不同的是,calloc函数所申请的空间更类似于一个数组,也为一个连续的空间,且在申请空间后,calloc函数会将所申请的空间初始化为0

calloc函数与malloc函数与之不同的是参数不同以及在开辟空间后calloc函数会将所开辟的空间初始化为0

calloc函数的参数共有两个:

  • size_t num
    无符号类型的需要开辟空间的个数。
  • size_t size
    每个空间的大小是多少(单位字节)。

这就是为什么相比之下calloc函数开辟动态内存空间更类似于创建一个数组

int main()
{
  //开辟
   int*p= (int*)calloc(10,sizeof(int));
   if (p == NULL) {
     printf("%s\n", strerror(errno));
     return 1;
   }
   //使用
   //释放
   free(p);
   //置空
   p = NULL;
  return 0;
}

calloc函数在开辟内存过程中也存在着失败的风险所以应在开辟后判断函数的返回值是不是为空指针NULL,若是为空指针则开辟失败。

同时,calloc函数与malloc函数在使用时相同已经用完的动态内存应及时进行释放,且指向已经释放空间的那个指针也将及时的置空,避免出现内存泄漏与出现野指针的问题


realloc函数

动态内存的方便不仅在于随用随开辟,不用则释放;同时也在于开辟的动态内存可以根据使用需求进行适应的大小调整

若是在使用过程中觉得开辟的内存不够使用需要扩大内存时则需要使用realloc函数来调整开辟的动态内存大小

由图可知,realloc函数内共有两个参数:

  • void*ptr
    需要改变的内存块的起始位置地址。
  • size_t size
    需要将内存块改变的大小。

且该函数返回类型也为void*。

该函数若是修改成功时,则会返回这个新空间的起始地址。

失败则会返回一个空指针


int main()
{
  //创建
  int* p = (int*)malloc(20);
  if (p == NULL) {
    return 1;
  }
  //使用
  for (int i = 0; i < 5; i++) {
    *(p + i) = i + 1;
  }
  printf("realloc之前:");
  for (int i = 0; i < 5; i++) {
    printf("%d ", *(p + i));
  }
  //realloc扩大
  p = (int*)realloc(p, 40);
  //释放
  free(p);
  //对指针进行置空
  p = NULL;
  return 0;
}

上面代码在使用realloc函数中有一定问题,若是以上面的代码编写程序,会出现什么问题呢?

  • 未判断函数返回值是否为空指针(修改大小是否成功):
    realloc函数与malloc函数或calloc函数相同,也存在着失败的风险,故应在修改内存后判断函数的返回值是否为空指针,若是为空指针则代表在扩大空间的过程中出现了错误。
  • 直接使用指向malloc函数开辟空间的指针接收:
    直接使用原指针将改动后的空间地址进行接收,若是空间开辟失败,返回空指针,将会使原空间内的数据丢失,同时也将造成内存泄漏。

正确的使用方法√:

int main()
{
  //创建
  int* p = (int*)malloc(20);
  if (p == NULL) {
    return 1;
  }
  //使用
  for (int i = 0; i < 5; i++) {
    *(p + i) = i + 1;
  }
  printf("realloc之前:");
  for (int i = 0; i < 5; i++) {
    printf("%d ", *(p + i));
  }
  //realloc扩大
  int*ptr = (int*)realloc(p, 40);
  if (ptr != NULL) {
    p = ptr;
  }
  else {
    printf("%s\n", strerror(errno));
    return 1;
  }
  //释放
  free(p);
  //对指针进行置空
  p = NULL;
  return 0;
}

使用一个临时的指针接收函数返回的地址,并判断空间是否开辟(修改)成功(判断临时指针是否未空指针),若是成功,则使用原指针来接收该临时指针所指向的地址,否则提前退出程序或者使用原数据不再进行增容。

realloc函数的原理

在使用realloc函数时,应该明白该函数在扩容过程中,扩容成功会有两种情况:

若是在内存中,原动态开辟空间后的内存足够用来扩容这块空间时,将会直接在该空间后的内存进行扩容

同时函数会返回原指针的地址


若是原动态开辟空间后的内存不足进行扩容,realloc函数则会再其他位置重新再申请一块空间

并将原空间内的数据拷贝至新的空间,并对原来的内存进行释放

同时函数将返回新空间的地址

注意事项

  • 在使用动态内存函数开辟空间时,应注意在使用时要判断开辟空间是否成功,若是不成功则可能导致程序崩溃。
  • 使用calloc、malloc、realloc函数开辟的动态内存应在用完后及时进行释放,如果不释放,在程序结束之后操作系统也会将这块空间进行回收;若是程序未结束,空间也没有释放,则会出现内存泄露的现象。
  • 释放了内存后应及时将指向这块空间的指针置为空指针,确保指针不会在空间释放后变为野指针。
  • 动态内存不能被部分释放。
  • free函数不能用来释放非动态开辟内存,若是free函数释放非动态开辟内存,程序将崩溃。


相关文章
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
38 3
|
15天前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
31 8
|
15天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
47 6
|
1月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
47 6
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
2月前
|
存储 缓存 监控
深入了解MySQL内存管理:如何查看MySQL使用的内存
深入了解MySQL内存管理:如何查看MySQL使用的内存
421 1
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
34 0
|
2月前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
43 0
|
2月前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
75 0