服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言深入探索动态内存分配的使用

C语言深入探索动态内存分配的使用

2022-11-10 15:03清风自在 流水潺潺 C/C++

给数组分配多大的空间?你是否和初学C时的我一样,有过这样的疑问。这一期就来聊一聊动态内存的分配,读完这篇文章,你可能对内存的分配有一个更好的理解

一、动态内存分配的意义

C语言中的一切操作都是基于内存的

变量和数组都是内存的别名

  • 内存分配由编译器在编译期间决定
  • 定义数组的时候必须指定数组长度
  • 数组长度是在编译期就必须确定的

需求:程序运行的过程中,可能需要使用一些额外的内存空间

二、malloc 和 free

malloc 和 free 用于执行动态内存分配和释放

C语言深入探索动态内存分配的使用

  • malloc 所分配的是一块连续的内存
  • malloc 以字节为单位,并且不带任何的类型信息
  • free 用于将动态内存归还系统

void* malloc(size_t size);

void free(void* pointer);

注意事项

  • malloc 和 free 是库函数,而不是系统调用
  • malloc 实际分配的内存可能会比请求的多
  • 不能依赖于不同平台下的 malloc 行为
  • 当请求的动态内存无法满足时 malloc 返回 NULL
  • 当 free 的参数为 NULL 时,函数直接返回

下面看一个内存泄漏检测模块的示例:

test.c:

#include <stdio.h>
#include "mleak.h"

void f()

{
  MALLOC(100);
}

int main()
{
  int* p = (int*)MALLOC(3 * sizeof(int));

  f();

  p[0] = 1;
  p[1] = 2;
  p[2] = 3;    

  FREE(p);    

  PRINT_LEAK_INFO();   

  return 0;

}

mleak.h:

#ifndef _MLEAK_H_

#define _MLEAK_H_

#include <malloc.h>

#define MALLOC(n) mallocEx(n, __FILE__, __LINE__)

#define FREE(p) freeEx(p)


void* mallocEx(size_t n, const char* file, const line);

void freeEx(void* p);

void PRINT_LEAK_INFO();

#endif

mleak.c:

#include "mleak.h"

#define SIZE 256


/* 动态内存申请参数结构体 */

typedef struct
{
  void* pointer;
  int size;
  const char* file;
  int line;

} MItem;

static MItem g_record[SIZE]; /* 记录动态内存申请的操作 */

void* mallocEx(size_t n, const char* file, const line)
{
  void* ret = malloc(n); /* 动态内存申请 */   

  if( ret != NULL )
  {
      int i = 0;       

      /* 遍历全局数组,记录此次操作 */

      for(i = 0; i < SIZE; i++)
      {

          /* 查找位置 */

          if( g_record[i].pointer == NULL )
          {
              g_record[i].pointer = ret;
              g_record[i].size = n;
              g_record[i].file = file;
              g_record[i].line = line;
              break;

          }

      }

  }   

  return ret;

}

void freeEx(void* p)
{
  if( p != NULL )
  {
      int i = 0;        

      /* 遍历全局数组,释放内存空间,并清除操作记录 */

      for(i = 0; i < SIZE; i++)
      {
          if( g_record[i].pointer == p )

          {

              g_record[i].pointer = NULL;

              g_record[i].size = 0;

              g_record[i].file = NULL;

              g_record[i].line = 0;                

              free(p);                

              break;

          }

      }

  }

}

void PRINT_LEAK_INFO()
{

  int i = 0;   

  printf("Potential Memory Leak Info:\n");    

  /* 遍历全局数组,打印未释放的空间记录 */

  for(i = 0; i < SIZE; i++)
  {
      if( g_record[i].pointer != NULL )
      {
          printf("Address: %p, size:%d, Location: %s:%d\n", g_record[i].pointer, g_record[i].size, g_record[i].file, g_record[i].line);
      }
  }

}

输出结果如下, 因为 MALLOC(100); 之后没有进行释放内存,所以被检查出来了。

C语言深入探索动态内存分配的使用

暂时不能用于工程开发,需要再开发才行。因为 malloc 往往在不同的线程中被调用,因此 malloc 函数必须要有互斥的操作。因为 static MItem g_record[SIZE]; 这个静态全局数组是一种临界区,必须被保护起来。

 

三、关于 malloc(0)

malloc(0); 将返回什么?

下面看一段代码:

#include <stdio.h>
#include <malloc.h>

int main()
{
  int* p = (int*) malloc(0);
  
  printf("p = %p\n", p);
  
  free(p);

  return 0;
}

输出结果如下:

C语言深入探索动态内存分配的使用

这说明 malloc(0) 是合法的,内存地址其实包含两个概念,一个是内存的起始地址,一个是内存的长度。在平常我们可能会只注意内存的首地址,对于长度却忽略了。malloc(0) 在这个程序中申请到的内存起始地址为0x82c3008,长度为 0。

但是我们在程序里不停写 malloc(0),会造成内存泄漏吗?答案是肯定的,因为malloc 实际分配的内存可能会比请求的多,目前的操作系统一般都是 4 字节对齐的,所以写 malloc(0) 系统实际返回的字节数也许就是 4 字节。

 

四、calloc 和 realloc

malloc 的同胞兄弟

void* calloc(size_t num, size_t size);

void* realloc(void* pointer, size_t new_size);

calloc 的参数代表所返回内存的类型信息

  • calloc 会将返回的内存初始化为 0

realloc 用于修改一个原先已经分配的内存块大小

  • 在使用 realloc 之后应该使用其返回值
  • 当 pointer 的第一个参数为 NULL 时,等价于 malloc

下面看一个 calloc 和 realloc 的使用示例:

#include <stdio.h>
#include <malloc.h>

#define SIZE 5

int main()
{
  int i = 0;
  int* pI = (int*)malloc(SIZE * sizeof(int));
  short* pS = (short*)calloc(SIZE, sizeof(short));
  
  for(i = 0; i < SIZE; i++)
  {
      printf("pI[%d] = %d, pS[%d] = %d\n", i, pI[i], i, pS[i]);
  }
  
  printf("Before: pI = %p\n", pI);
  
  pI = (int*)realloc(pI, 2 * SIZE * sizeof(int));
  
  printf("After: pI = %p\n", pI);
  
  for(i = 0; i < 10; i++)
  {
      printf("pI[%d] = %d\n", i, pI[i]);
  }
  
  free(pI);
  free(pS);
  
  return 0;
}

输出结果如下:

C语言深入探索动态内存分配的使用

malloc 只负责申请空间,不负责初始化,这里的pI 指针保存的值均为 0 只是巧合罢了,另外使用 realloc 重置之后,内存地址也会改变,pI 指针保存的值也会改变,这里都为 0 同样也是巧合。

五、小结

  • 动态内存分配是 C 语言中的强大功能
  • 程序能够在需要的时候有机会使用更多的内存
  • malloc 单纯的从系统中申请固定字节大小的内存
  • calloc 能以类型大小为单位申请内存并初始化为0
  • realloc 用于重置内存大小

到此这篇关于C语言深入探索动态内存分配的使用的文章就介绍到这了,更多相关C语言 动态内存分配内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_43129713/article/details/124035850

延伸 · 阅读

精彩推荐
  • C/C++C++ Strassen算法代码的实现

    C++ Strassen算法代码的实现

    这篇文章主要介绍了C++ Strassen算法代码的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着...

    Shiroha6982021-08-30
  • C/C++C++中的friend函数详细解析

    C++中的friend函数详细解析

    本篇文章主要介绍了C++中的friend函数详细解析,对初学c++的人有一定的帮助,有需要的可以了解一下。...

    麻木了11942021-04-19
  • C/C++详解C++编程中类的声明和对象成员的引用

    详解C++编程中类的声明和对象成员的引用

    这篇文章主要介绍了详解C++编程中类的声明和对象成员的引用,是C++入门学习中的基础知识,需要的朋友可以参考下...

    C++教程网7072021-03-13
  • C/C++C++实现合并排序的方法

    C++实现合并排序的方法

    这篇文章主要介绍了C++实现合并排序的方法,实例分析了合并排序的原理与相关实现技巧,需要的朋友可以参考下...

    Jack_Wong20105392021-03-04
  • C/C++《战狼》中两军作战入侵代码竟然是输出星期几的!

    《战狼》中两军作战入侵代码竟然是输出星期几的!

    这篇文章主要介绍了《战狼》中两军作战入侵代码竟然是输出星期几的,喜欢战狼和编程的同学可以了解下。...

    mengwei3882021-06-09
  • C/C++Qt编写地图实现动态点位标注的示例代码

    Qt编写地图实现动态点位标注的示例代码

    动态点位标注是定制的一个功能模块,提供直接地图上选点设置标记点,点位信息用结构体存储,其中包括了经度、纬度、速度、时间等信息。本文将介绍...

    feiyangqingyun4412022-08-30
  • C/C++解析Linux下C++编译和链接

    解析Linux下C++编译和链接

    编译&链接对C&C++程序员既熟悉又陌生,熟悉在于每份代码都要经历编译&链接过程,陌生在于大部分人并不会刻意关注编译&链接的原理。本文通过开发过程...

    华为云开发者社区10122021-11-08
  • C/C++Qt数据库应用之实现数据的导入与导出

    Qt数据库应用之实现数据的导入与导出

    QT中涉及到数据库相关的项目,几乎都需要将少量的信息数据导出到文件保存好,然后用户可以打开该表格进行编辑,编辑完成后保存,再重新导入到软件...

    feiyangqingyun8582022-08-14