C 语言中开辟动态内存的有三个函数,分别为 malloc,calloc,realloc,释放内存的只有一个函数 free。
realloc的使用是最容易犯错的,在写这篇博客前老师让我把realloc的使用和注意事项仔细搜资料看看,在网上看了几篇博客之后还是决定写一篇博客,把这些东西都记录下来。
测试程序是否有内存泄漏推荐使用开源工具 vld,vld的安装和使用请点击此处跳转到vld教程博客

1.malloc的使用

malloc 使用的最频繁,因为它最简单,只需要一个参数,即需要动态开辟的内存的字节数,如果堆里的连续空间能满足需要则将分配好的内存首地址返回,否则返回 NULL。
malloc()函数的实质,它可以将可用的内存块连接成一个长长的链表,被称为空闲链表,当用户调用malloc函数时,它沿着链表找到比用户申请的内存的可用的内存块,如果内存块大了,它会把可用的内存一分为二,其中一块就是和用户申请的内存大小相等的内存块,它将这块内存块分配给用户(即将内存块首地址传给用户),另一块会返回到链表上。
调用free函数的时候,它会将用户释放的内存重新连接在链表上,注意,此时并没有和分隔开的第二块内存合并。最后空闲链表连接的内存可能是一小块一小块的,此时如果用户申请一个较大的内存块,那么这时候malloc函数就会找当前空闲内存,如果当前空闲内存无法满足用户申请的空间的大小,那么malloc就会将相邻的小内存块合并,如果最后将空闲的相邻的小内存块合并也无法满足用户申请的空间大小的要求,那么malloc函数就会返回NULL。
当我们用malloc申请空间时比如我们申请4字节大小空间,其实malloc内部,申请的空间要大于这个字节数。malloc需要在用户申请的空间的上部和下部多开辟4字节空间,作为上越界标记,和下越界标记,另外头部还有28字节空间用来记录空间使用情况等等,这些都是使用malloc的额外负担。

2.calloc的使用

函数原型:
void* calloc(int n, int size);
n是元素的数目
size是每个元素的大小
功能:在内存动态存储区中分配n块长度为size字节的连续区域calloc 只是在 malloc 的基础上将分配好的每个字节赋值为 0,这个功能使用并不多见,但由于 callloc 需要提供两个参数,相比较而言并没有 malloc 使用的多。

3.free的使用

free,用于释放内存,主要注意 free 可能引发程序崩溃的几个原因:
1、越界;
2,移动指针的指向,free 时指针不指向动态内存的开头;
3、重复释放同一段内存;
4、释放不是动态创建的内存。

4.realloc的使用

realloc原型
void* realloc(void* ptr,unsigned int newsize);
功能:改变ptr所指向的内存区域的大小为newsize的长度,并且将原内存中的数据拷贝到新内存中。

4.1realloc扩大内存的方式

我们都知道realloc是从堆上申请空间,当扩大一块内存空间时,realloc会先试图直接从现申请的堆空间后面进行扩充,如果现申请的堆空间后面有空间足够用于扩充,那么皆大欢喜,realloc直接在堆后面进行扩充。
但是如果堆后面的空间不够realloc扩充用呢?那么realloc就会在堆中重新找一块newsize大小的内存,然后将原内存的数据拷贝到新内存中,指针指向新内存。这说明一个问题,就是realloc扩充空间过后,原来指向内存的指针的指向很可能会有变化。
如果ptr为NULL,则realloc和malloc一样,分配一个newsize的空间,返回指向该空间首地址的指针。如果newsize大小为0,那么释放ptr所指向的内存,返回NULL。如果没有足够的可用的内存用来扩充,那么返回NULL,ptr指向的内存和数据都不改变。
我们通过对指针是否指向同一个地址的判断,来展现我们所说的现象。
下图这种情况属于直接在对后面空间扩充,导致p和q都指向堆空间。注意在这里我们没有对空间的释放,程序结束,空间自动释放,这里可能会出现悬挂指针的问题(即空间被释放,但是指针仍然指向这个空间),关于指针和空间释放的问题,我们在最下面再给出解决。这里只是说明realloc扩容的方式和对应的现象。

如下程序,我们只扩充了4字节,realloc发现已申请的堆空间后面有足够的空间,于是在堆后面紧接着扩充了4个字节。扩充后的空间的首地址和扩充前的空间的首地址一样,所以p和q指向了同一个空间。

#include<stdio.h>
#include<stdlib.h>int main()
{int* p = (int*)malloc(sizeof(int) * 10);int* q = (int*)realloc(p, sizeof(int) * 11);if (q == NULL){printf("realloc false\n");}if (q == p){printf("p and q point the same space\n");}else{printf("realloc find a new space\n");}return 0;
}


下面这种情况就属于重新分配的空间,我们扩充的空间较大,realloc发现堆后面的空间不够扩容,就重新申请了一个newsize大小的空间,q指向新空间首地址,p指向的空间被释放,成为悬挂指针。

#include<stdio.h>
#include<stdlib.h>int main()
{int* p = (int*)malloc(sizeof(int) * 10);int* q = (int*)realloc(p, sizeof(int) * 1024*1024*400);if (q == NULL){printf("realloc false\n");}if (q == p){printf("p and q point the same space\n");}else//p != q{printf("realloc find a new space\n");}/*free(q);free(p);*/return 0;
}

下面这种情况属于扩充失败,但是p指向的空间和数据都还存在,都不变都正常使用,realloc返回NULL,q == NULL。

#include<stdio.h>
#include<stdlib.h>int main()
{int* p = (int*)malloc(sizeof(int) * 10);int* q = (int*)realloc(p, sizeof(int) * 1024*1024*500);if (q == NULL){printf("realloc false\n");}else if (q == p){printf("p and q point the same space\n");}else{printf("realloc find a new space\n");}return 0;
}

4.2指针在 realloc时正确的使用方式

在讨论realloc中我们已经知道了realloc的扩容方式,以及指针存在的问题,对上面代码中的指针而言:
(1)如果扩容成功,p指针指向的堆空间将被释放,p指针不处理的话,将会成为悬挂指针。
(2)如果扩容失败,q指针将为NULL,p指针指向的空间和数据内容不变。
所以,我们要对指针做一些处理,如果扩容成功,我们将p指针指向NULL;如果扩容失败,我们不能盲目的将p指针指向NULL,否则的话,我们的空间会无法释放,数据会丢失。

int main()
{int* p = (int*)malloc(sizeof(int) * 10);int* q = (int*)realloc(p, sizeof(int) * 100);if (q == NULL){q = p;}p = NULL;free(q);
}

这样写就会解决掉悬挂指针和空间和数据可能会的情况。但是这样做不一定妥当,因为我们不知道是否空间申请成功了,所以我们如果一定要知道是否扩容成功,可以加断言判断等。但是一定要考虑悬挂指针问题,和空间和数据小心丢失问题,就没问题了。

4.3realloc缩小内存的使用

我们在realloc扩容方式中提出可能在原堆空间后面扩容,也可能重新申请堆空间,重新申请堆空间的话,空间首地址会发生改变。在这里不仅有人要想了,那扩容是因为可能寻找新的堆空间才导致空间首地址会发生变化,那么realloc缩小内存,不需要寻找新空间,直接在原来申请的堆空间上将后面的空间释放掉一部分就可以了,那是不是缩小内存的话空间首地址就一定不会改变了?
答案并非如此!
realloc可以在任何调用上移动内存,在许多实现中,收缩只会导致堆中保留大小的更改,并且不会移动内存。然而,在针对低碎片进行优化的堆中,它可以选择将储存器移动到更合适的位置。
所以,对于任何操作,都不要依赖realloc保持内存在同一个地方!
另外,realloc缩小内存的话,如果realloc(void* ptr,unsigned int newsize)中的newsize为0的话,那么ptr指向的空间被释放,realloc返回NULL。如果只是简单的缩小内存,那么原内存中超过newsize大小的空间中的数据会丢失。

malloc、calloc、realloc和free相关推荐

  1. malloc calloc realloc的对比

    函数原型 三个函数的声明分别是: void* realloc(void* ptr, unsigned newsize); void* malloc(unsigned size); void* call ...

  2. 内存分布malloc/calloc/realloc/free/new/delete、内存泄露、String模板、浅拷贝与深拷贝以及模拟string类的实现

    内存分布 一.C语言中的动态内存管理方式:malloc/calloc/realloc和free 1.malloc: 从堆上获得指定字节的内存空间,函数声明:void *malloc (int n); ...

  3. C语言动态申请内存空间之malloc(),calloc(),realloc()函数

    在C语言中用于动态申请内存空间的函数主要为malloc()函数,calloc()函数,以及realloc()函数,以下主要介绍三个函数的用法,区别以及使用时的注意事项. malloc(),calloc ...

  4. c语言malloc,calloc,realloc函数介绍

    malloc,calloc,realloc动态内存管理函数的出现解决了在某些c语言标准中不能使用变长数组的问题 这三个函数的使用需要头文件stdlib.h,这些函数开辟的空间在堆区,系统不会自动释放, ...

  5. malloc/calloc/realloc

    malloc/calloc/realloc和free介绍: malloc函数 malloc函数可以从堆上获得指定字节的内存空间,其函数声明如下: void * malloc(int n); 参数释义: ...

  6. malloc calloc realloc 三兄弟!!!

    学习过C语言的都知道 malloc calloc realloc 这三个函数,这三个函数都是用于动态的分配内存的,最后都要使用 free函数进行释放. 这三个函数的头文件 : stdlib.h 在li ...

  7. c语言释放内存函数,【C语言】内存分配函数malloc/ calloc/ realloc及内存释放free

    前言: 内存区域划分与分配: 1.栈区(stack)--程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等,程序结束时由编译器自动释放. 2.堆区(heap) -- 在内存开辟另一块存储区域 ...

  8. malloc,calloc,realloc,free函数

    在进行C/C++编程的时候,需要程序员对内存的了解比较清楚,经常需要操作的内存可分为下面几个类别: 1.堆栈区(stack):由编译器自动分配与释放,存放函数的参数值,局部变量,临时变量等等,它们获取 ...

  9. 浅谈malloc,calloc,realloc函数之间的区别

    内存区域可以分为栈,堆,静态存储区和常量存储区.局部变量,函数形参,临时变量都是在栈上获得内存的,它们获取的方式都是由编译器自动执行的. C 标准函数库提供了许多函数来实现对堆上内存管理,其中包括:m ...

  10. malloc calloc realloc

    (类型*)malloc(size) (类型*)calloc(n,size) (类型*)realloc(*ptr,size) (1) malloc 分配指定字节数的存储区.此存储区中的初始值不确定 (2 ...

最新文章

  1. python慕课视频-python爬虫:爬取网站视频
  2. Sentinel: 分布式系统的流量防卫兵 1
  3. CentOS 6.4配置TL-WN823N外置无线网卡
  4. Java高级开发工程师面试笔记
  5. 动态规划算法实验报告_强化学习之动态规划算法
  6. c iostream.源码_通达信指标公式源码精准买卖主图指标公式免费分享
  7. CUHK interview in Shen Zhen
  8. Android下异步扫描视频文件缩略图
  9. 1.微信小程序(colorUI)- 引入colorUI
  10. ppt 制作海报 导出高分辨率图片
  11. ap启动失败 ensp_eNSP模拟WLAN——上线华为真实AP的方法及配置
  12. oracle 分区交换
  13. Android获取手机Cpu温度
  14. python 股票竞价数据_GitHub - TruthHun/auction-stock: 集合竞价选股(股票),基于收盘价与前收盘价的选股策略...
  15. PureMVC框架应用简述
  16. Helix --未完待续
  17. Unity抽奖转盘制作代码
  18. 网站建设(3)——CDN及CDN加速原理
  19. Python--nonzero()函数
  20. 千峰Ajax【fetch和promise】

热门文章

  1. 【原创】民航业——软件项目管理经验点滴
  2. [ SAP ASAP ] SAP ASAP 方法论 对SAP项目文档的考核标准、 Documentation Principle
  3. 用Unity3D实现可展开公告版
  4. 1356. Sort Integers by The Number of 1 Bits 根据数字二进制下 1 的数目排序
  5. 全国大学生电子设计大赛 赛前准备
  6. mybatis应用(二)注解的实现
  7. 【Linux网络编程】组播
  8. 【C++】 C++标准模板库(五)Stack
  9. [Qt教程] 第27篇 XML(一)使用DOM读取XML文档
  10. linux 杀死t状态进程,Linux下如何查杀stopped进程