详解C/C++动态内存函数(malloc、free、calloc、realloc)

简介: 详解C/C++动态内存函数(malloc、free、calloc、realloc)

1. malloc和free

为解决静态内存开辟存在的问题,C语言提供了一个动态内存开辟的函数:

malloc为memory allocation的简写,意为内存分配。

这个函数的作用是向内存申请一块连续可用的空间,并返回指向这块空间的指针。

比如,我申请40个字节的空间,以前我们可以用数组的方法,现在我们用malloc函数申请40个字节的空间:

  //int arr[10];
  int* p = (int*)malloc(40);

malloc函数有以下特点:

如果开辟成功,则返回一个指向开辟好空间的指针;

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查;

返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定;

如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

所以我们需要对malloc开辟的空间做检查:

 
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");//如果开辟失败,告知原因
    exit(-1);//开辟失败直接退出
  }

需要注意的是:

对于malloc函数申请的内存空间,当程序退出时还给操作系统;当程序不退出时,malloc动态申请的内存不会主动释放。


所以这时C语言提供了另外一个函数free,专门用来做动态内存的释放和回收:

用法如下:

int main()
{
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");//如果开辟失败,告知原因
    exit(-1);//开辟失败直接退出
  }
  free(p);
    p = NULL;//避免p被释放后成为野指针
  return 0;
}

需要注意的是:

free函数用来释放动态开辟的内存;

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的(也就是说free函数只能释放动态开辟的空间,不能释放静态开辟的空间);

如果参数 ptr 是NULL指针,则free函数什么事都不做。

(malloc和free都声明在stdlib.h头文件中)


2. calloc

C语言还提供了一个函数叫calloc ,calloc为contiguous allocation的简写,意为动态内存分配并清零,calloc函数也用来动态内存分配。

原型如下:

函数的功能是为 num个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0;

与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

具体用法与malloc大致相同:

int main()
{
  int* p = (int*)calloc(10, sizeof(int));//10个大小为4字节的元素
  if (p == NULL)
  {
    perror("calloc");//如果开辟失败,告知原因
    exit(-1);//开辟失败直接退出
  }
  //打印已开辟好的元素
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", p[i]);
  }
    free(p);
    p = NULL;//避免p被释放后成为野指针
  return 0;
}


3. realloc

在我们使用malloc函数与calloc函数申请过空间之后,

我们可能会遇到申请的空间过大了,比如我动态申请了1000个字节的空间,可发现我只需要10个字节的空间;

又有可能遇到申请的空间过小了,比如我动态申请了100个字节的空间,可最后发现我却要10000字节的空间;

针对这两种现象,为了合理地使用内存,我们一定会对内存空间做灵活地调整,那 realloc 函数就可以做到对动态开辟内存大小的调整。

realloc函数原型如下:

解释:

1.ptr 是要调整的内存地址;

2.size 调整之后新大小;

3.返回值为调整之后的内存起始位置;

4.这个函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

关于第3条,我们抛出这样的疑问:

返回值为调整之后的内存起始位置,那么这个调整之后的起始位置,

①到底是realloc函数保留了原来malloc/realloc函数已经开辟好的空间,只是在原来内存的基础上紧挨着新增了一些空间,然后返回了原来旧内存空间的起始地址;

②还是realloc函数舍弃了原来malloc/realloc函数已经开辟好的空间,又再另一块地方重新开疆拓土,申请了一块新的内存空间,然后返回了新内存空间的起始地址。

对于这个问题,其实两种说法都对,我们要分两种情况讨论:

情况一:原有空间之后有足够大的空间。

情况二:原有空间之后的空间不足

1.开辟新的空间;

2.将旧的空间中的数据拷贝到新的空间;

3.释放旧的空间;

4.返回新空间的起始地址。

所以针对情况一和情况二,对realloc函数的正确用法应为:

int main()
{
  //先用malloc开辟40个字节的空间
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");//如果开辟失败,告知原因
    exit(-1);//开辟失败直接退出
  }
  //开辟成功后,将这块空间初始化为1~10
  for (int i = 0; i < 10; i++)
  {
    p[i] = i + 1;
  }
  //再用realloc再增加40个字节的空间
  int* ptr = (int*) realloc(p, 80);//未避免情况二的发生,不能用p接收新地址
  if (ptr != NULL)
  {
    p = ptr;//如果开辟成功了,再把ptr拷贝给p
    ptr = NULL;
  }
  else
  {
    perror("realloc");//如果开辟失败,告知原因
  }
  //打印数据
  for (int i = 0; i < 20; i++)
  {
    printf("%d\n", p[i]);
  }
  //释放空间
  free(p);
  p = NULL;
  return 0;
}

运行结果:


验证:

情况一:新开辟的空间比较小,后面有足够的空间,(新开辟4字节):

情况二:原有空间之后的空间不足(新开辟400字节)

(小知识:如果p为空指针,那么realloc与malloc的功能相同;如果缩小空间,那么就一定是情况一了。)

(本篇完)

文章知识点与官方知识档案匹配

目录
打赏
0
1
1
0
12
分享
相关文章
玩转C++内存管理:从新手到高手的必备指南
C++中的内存管理是编写高效、可靠程序的关键所在。C++不仅继承了C语言的内存管理方式,还增加了面向对象的内存分配机制,使得内存管理既有灵活性,也更加复杂。学习内存管理不仅有助于提升程序效率,还有助于理解计算机的工作原理和资源分配策略。
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
252 68
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
167 0
什么是内存泄漏?C++中如何检测和解决?
大家好,我是V哥。内存泄露是编程中的常见问题,可能导致程序崩溃。特别是在金三银四跳槽季,面试官常问此问题。本文将探讨内存泄露的定义、危害、检测方法及解决策略,帮助你掌握这一关键知识点。通过学习如何正确管理内存、使用智能指针和RAII原则,避免内存泄露,提升代码健壮性。同时,了解常见的内存泄露场景,如忘记释放内存、异常处理不当等,确保在面试中不被秒杀。最后,预祝大家新的一年工作顺利,涨薪多多!关注威哥爱编程,一起成为更好的程序员。
115 0
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
119 3
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
372 4
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
353 22
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
7月前
|
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
81 0
【C++打怪之路Lv6】-- 内存管理

热门文章

最新文章