深入探索C语言动态内存分配:释放你的程序潜力

简介: 深入探索C语言动态内存分配:释放你的程序潜力

🌈大家好!我是Kevin,蠢蠢大一幼崽,很高兴你们可以来阅读我的博客!

🌟我热衷于分享🖊学习经验,🏫多彩生活,精彩足球赛事⚽

🌟感谢大家的支持,期待我们共同进步💞



动态内存分配的意义

假如我们创建一个存放一百个元素的数组:

char arr[100] = {0};

如果我们要用这个数组来存储数据的话只能存储100个char型的数据,如果再想要往数组内添加数据的话就会越界。

所以在这个时候有两种方法,第一个方法是再创建一个数组然后将两个数组拼接,第二个方法时用动态内存分配。动态内存分配的快捷,实用性和可操控要比第一种方法强很多,那么究竟强在哪里呢?在这篇博客中我将进行具体阐述。

动态内存分配相关函数

1. malloc

malloc函数用来向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。

该函数原型如下:

void* malloc (size_t size);

可以看出,该函数的返回值是void*说明返回类型可以改变,传的参数size作用是向内存申请size大小的内存,用size_t类型可以不用担心申请的内存过大而不能完成申请。

对于开辟内存的结果和操作:

  • 如果开辟成功,则返回⼀个指向开辟好空间的指针。
  • 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
  • 返回值 的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
  • 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器

2. free

free函数用来释放动态分配的内存(只能释放动态分配的内存),函数原型如下:

void free (void* ptr);
  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
  • 如果参数 ptr 是NULL指针,则无事发生。

在了解到malloc和free函数后就可以利用这两个函数进行一个简单的实例代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int num = 0;
    scanf("%d", &num);
    int arr[num] = {0};
    int* ptr = NULL;
    ptr = (int*)malloc(num*sizeof(int));
    if(NULL != ptr)//判断ptr指针是否为空
    {
      int i = 0;
        for(i=0; i<num; i++)
        {
          *(ptr+i) = 0;
        }
    }
    free(ptr);//释放ptr所指向的动态内存
    ptr = NULL;//是否有必要?
    return 0;
}

该段代码就是实现动态分配,然后释放分配的空间。

3. calloc

calloc 函数也⽤来动态内存分配。原型如下:

void* calloc (size_t num, size_t size);
  • 函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0.

示例代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  int* p = (int*)calloc(10, sizeof(int));
  if (NULL != p)
  {
    int i = 0;
    for (i = 0; i < 10; i++)
    {
      printf("%d ", *(p + i));
    }
  }
  free(p);
  p = NULL;
  return 0;
}

示例代码结果:

0 0 0 0 0 0 0 0 0 0

从函数的特性出发,如果我们对函数有初始化的需求的话可以直接使用 calloc 函数来进行内存空间的开辟,效果与 malloc 一样。

4. realloc

realloc 函数实现的是对原本开辟的空间进行调整大小,函数原型如下:

void* realloc (void* ptr, size_t size); 
  • ptr 是要调整的内存地址
  • size 调整之后新⼤⼩
  • 返回值为调整之后的内存起始位置。
  • 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动(copy)到新的空间。
  • realloc在调整内存空间的是存在两种情况:

情况1:原有空间之后有⾜够⼤的空间

情况2:原有空间之后没有⾜够⼤的空间

以上所说两种空间是在内存上的空间,不是当前动态分配到的空间

4.1 原有空间足够大

当原有空间足够大时,我们要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。

4.2 原有空间不够

当原有的空间不够时,我们在堆空间上另找⼀个合适大小的连续空间来使⽤。这样函数返回的是⼀个新的内存地址。

realloc使用示例代码:

#include <stdlib.h>
int main()
{
  int* ptr = (int*)malloc(100);
  if (ptr != NULL)
  {
    //业务处理
  }
  else
  {
    return 1;
  }
  //扩展容量
  //代码1 - 直接将realloc的返回值放到ptr中
  ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
  //代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中
  int* p = NULL;
  p = realloc(ptr, 1000);
  if (p != NULL)
  {
    ptr = p;
  }
  //业务处理
  free(ptr);
  return 0;
}

常见动态内存错误

1. 对NULL指针解引用

void test()
{
  int* p = (int*)malloc(INT_MAX / 4);
  *p = 20;//如果p的值是NULL,就会有问题
  free(p);
}

2. 对动态开辟空间的越界访问

void test()
{
  int i = 0;
  int* p = (int*)malloc(10 * sizeof(int));
  if (NULL == p)
  {
    exit(EXIT_FAILURE);
  }
  for (i = 0; i <= 10; i++)
  {
    *(p + i) = i;//当i是10的时候越界访问
  }
  free(p);
}

3. 对⾮动态开辟内存使⽤free释放

void test()
{
  int a = 10;
  int *p = &a;
  free(p);//error
}

4. 使⽤free释放⼀块动态开辟内存的⼀部分

void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}

5. **对同⼀块动态内存多次释放 **

void test()
{
  int* p = (int*)malloc(100);
  free(p);
  free(p);//重复释放
}

6. 动态开辟内存忘记释放(内存泄漏)

void test()
{
  int* p = (int*)malloc(100);
  if (NULL != p)
  {
    *p = 20;
  }
}
int main()
{
  test();
  while (1);
}

结语

以上就是关于动态内存管理的博客内容。值得强调的是关于 free 掉内存这件事,一个程序如果不在不需要的时候free掉内存的话就会一直占用内存直到程序运行结束。

在我们平时的代码练习中不会有明显的影像,但是在大型程序中,如果内存一直占用,占用的内存不断增多,内存是有限的,不可能一直被占用,当内存爆满时程序就会出现问题了。

所以要注意关于内存空间的释放!


目录
相关文章
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3
|
1月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
25天前
|
NoSQL 测试技术
内存程序崩溃
【10月更文挑战第13天】
128 62
|
13天前
|
存储 缓存 Java
结构体和类在内存管理方面的差异对程序性能有何影响?
【10月更文挑战第30天】结构体和类在内存管理方面的差异对程序性能有着重要的影响。在实际编程中,需要根据具体的应用场景和性能要求,合理地选择使用结构体或类,以优化程序的性能和内存使用效率。
|
22天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
35 3
|
1月前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
103 21
|
1月前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
1月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
1月前
|
程序员 C语言
C语言内存函数精讲
C语言内存函数精讲
|
25天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
21 0