1 关于动态内存

C程序使用malloc、realloc等内存申请函数在上分配和释放内存的。

程序在执行期间有两个内存区域,一个是内存区域是栈(stack),另一个是堆(heap)。栈中的空间分配给函数的参数和本地变量,执行完该函数后,存储的参数和本地变量的内存空间就会自动释放。在堆上分配的内存,在不需要的时候要用free释放空间,由程序员控制。

使用realloc的好处:不需要预先分配存储空间,且可以按需扩大或缩小,有效的使用内存空间。

使用realloc的坏处:会占用大量CPU资源。一般情况下应避免调用realloc。

2 函数介绍

头文件:

#include<stdlib.h>

#include<malloc.h>(我用的ubuntu gcc不用这个头文件)

原型:

extern void *realloc(void *mem_address,unsigned int newsize);

mem_address:一个需要重新分配内存空间的指针。

newsize:内存块新的大小。

功能(来源:百度百科,加黑内容在后文探究):

先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来的指针是自动释放,不需要使用free[后面对此进行探究]),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

返回值:

重新分配成功,返回指向被分配内存的指针。

失败返回空指针NULL。

需要特别注意的地方:

1.当内存不再使用时候,一定一定记得要用free()函数释放。

2.执行失败,realloc()返回NULL,对原mem_address指针指向的内存块则原封不动。

3.realloc新分配的内存,不会对额外分配的字节进行初始化。

4.realloc(ptr,0);相当于free(ptr);malloc(0);

5.realloc(NULL,size);相当于malloc(size);

6.一定要避免ptr=realloc(ptr,size);这样的代码,一旦分配出错原指针就出现内存泄露了。

7.传给free()为一个空指针,那么函数将什么都不做,并不是一个错误代码。而将非空ptr free()两次,将出现不可预计的错误。所以调用完free()后一定要加上一句代码ptr=NULL;

3 值得注意的地方代码探究

3.1 一定要避免ptr=realloc(ptr,size);这样的代码,一旦分配出错原指针就出现内存泄露了。

#include<stdio.h>
#include<stdlib.h>
int main()
{char *ptr;char *ptr_b;ptr = (char *)malloc(10);if(!ptr)return 1;strcpy(ptr,"say hello");ptr_b = ptr;printf("1 ptr = %p,ptr_b = %p\n",ptr,ptr_b);ptr = (char *)realloc(ptr,9999999999999999999);   printf("2 ptr = %p,ptr_b = %p\n",ptr,ptr_b);free(ptr);printf("ptr_b = %s\n",ptr_b);return 0;
}

如果用原指针来保存realloc的返回值,一旦出错,原指针将被覆盖,原空间内容依旧存在,程序虽然没有异常,但是已经内存泄露了,一定要养成良好习惯,再新增一个备份指针在realloc前把原指针的地址备份一下。后面还可以再把备份指针赋值给原指针。

3.2 realloc如果重新开辟了空间,原指针内存会被释放吗?还需要再调用free释放原指针吗?

这个我在ubuntu linux环境和windows dev c++64位编译器都尝试一下,有一些小小的区别

Linux:

#include<stdio.h>
#include<stdlib.h>
int main()
{char *ptr;char *ptr_b;ptr = (char *)malloc(10);if(!ptr)return 1;memset(ptr,0,10);strcpy(ptr,"say hello");ptr_b = ptr;printf("1 ptr = %p,ptr_b = %p\n",ptr,ptr_b);ptr_b = (char *)realloc(ptr,99999);  //指针发生改变printf("2 ptr = %p,ptr_b = %p\n",ptr,ptr_b);printf("ptr = %s,ptr_b=%s\n",ptr,ptr_b);free(ptr);//释放原始指针printf("free ptr\n");free(ptr_b);printf("free ptr_b\n");printf("END\n");return 0;
}

Windows:

可以看到,realloc重新开辟了内存空间,且内存块的地址也发生了改变,

在Linux中调用free函数释放原ptr指针直接发生段错误,显示二次清空,说明realloc重新申请地址块成功后会释放掉原地址块。即不用再调用free释放原始指针。(为什么会发生段错误见探究3.3)

但是Windows编译器并没有出现任何问题,咱也不清楚是不是编译器的问题,看起来一切正常,不过为了可移植性还是不要画蛇添足free原始指针了。

3.3 free空指针或者free两次会出问题吗?

#include<stdio.h>
#include<stdlib.h>
int main()
{char *ptr;char *ptr_b=NULL;ptr = (char *)malloc(10);if(!ptr)return 1;free(ptr_b);free(ptr_b);printf("ptr_b END\n");free(ptr);printf("1 ptr END\n");free(ptr);printf("2 ptr END\n");return 0;
}

结果可见,free空指针无论free多少次都没问题,但是,一个非空指针free两次程序就崩溃了。所以free指针后一定要手动再给指针赋值NULL,保险。

再回到探究3.2,realloc重新开辟了空间原指针会自动free吗,从linux环境试验结果来说,是的,不用再手动调用free函数了。(源码里确实调用了kfree释放了)

4 关于free的小知识

一个有趣的小知识,当调用free的时候,又没给他传参内存块的大小,那他是如何知晓内存块的大小的呢?

当分配内存块时,会额外分配几个字节来存放这块大小的整数值。该整数位于内存块的起始处,而实际返回给调用者的内存地址恰好位于这一长度记录字节之后。

5 realloc源码

void *realloc(void *ptr, size_t size)
{memblock_t *m;void *new = NULL;if (ptr) {if (!(m = memblock_get(ptr))) {printk(KERN_ERR "bug: realloc non-exist memory\n");return NULL;}if (size == m->size)return ptr;if (size != 0) {if (!(new = kmalloc(size, GFP_KERNEL)))return NULL;memmove(new, ptr, m->size);if (memblock_add(new, size)) {kfree(new);return NULL;}}memblock_del(m);kfree(ptr);} else {if (size != 0) {if (!(new = kmalloc(size, GFP_KERNEL)))return NULL;if (memblock_add(new, size)) {kfree(new);return NULL;}}}return new;
}

6 一个程序示例

有一个需求,有多条输出语句,但不能逐条输出,程序结束前一次性全部输出。

思路:申请字符指针,然后每次将输出的内容存在指针中,当指针内存不够时用realloc扩大内存,结束前打印输出即可。

关于va_list/va_start/va_end内容查看我的另一篇文章va_list可变参数理解(va_start/va_end...)

#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
//注意第一个参数传入的是指针的指针,不然realloc指针地址改变,下次传入进来的还是原指针地址
int ckx_sprintf (char** buf_p,int offset,int *total_size,const char *format, ...)
{va_list args;int len = 0;int available_size=0;int total_size_temp = 0;char *buf = *buf_p;//临时存一份,如果地址改动,结尾再赋值回去.char *r_buf = NULL;total_size_temp = *total_size;//当前buf总空间available_size = total_size_temp - offset;//当前buf可用空间va_start (args, format);//linux环境写入长度大于available_size,len还是为format长度//windows环境下返回-1len = vsnprintf (buf+offset, available_size, format, args);va_end (args);printf("len = %d total_size_temp = %d available_size = %d offset = %d\n",len,total_size_temp,available_size,offset);if (len < 0 || len >= available_size)//如果出错或长度大于当前可用空间{while (1){if (len > -1)total_size_temp = len + 1+total_size_temp;elsetotal_size_temp = total_size_temp *2;r_buf = (char*)realloc(buf, total_size_temp);if(!r_buf)return -1;if(r_buf != buf){//不用手动调用free(buf);buf = r_buf;//重新开辟了空间就将新指针赋值给旧指针。      }buf[offset] = '\0';*total_size = total_size_temp;available_size = total_size_temp - offset;va_start (args, format);len = vsnprintf (buf+offset, available_size, format, args);va_end (args);if (len > -1 && len < total_size_temp)//如果空间还是不够则继续循环break;}}*buf_p = buf;return len;
}
int main()
{char *str = NULL;int size = 10;int offset = 0;int i = 0;int size_add = 0;str = (char *)malloc(10);if(!str)return -1;for(i=0;i<50;i++){size_add = ckx_sprintf(&str,offset,&size,"i=%d,%s\n",i,"************");if(size_add < 0)return -1;offset += size_add;}printf("%s\n",str);free(str);str=NULL;//很重要,要养成习惯return 0;
}

结果:

主要是打印出来内存扩张的过程和vsnprintf在Linux和Windows下返回值的区别

Linux部分打印

Windows部分打印

7 重点总结回顾

  1. realloc重新分配了新地址,原指针无需调用free释放。
  2. free对同一非空指针调用两次,程序会崩溃,出现不可预计错误。
  3. 一定不要写ptr = realloc(ptr,size)这样入参和接受返回值都是同一指针的代码。
  4. realloc很占资源,能不用就不用。

realloc动态内存调整探究相关推荐

  1. c语言代码re通常什么错误,C语言,realloc动态内存申请,出现报错double free or corruption (!prev)...

    程序大概如下: #include #include #include #include #include #define MALLOC(num,type) (type*)alloc((num)*siz ...

  2. 动态内存管理(开辟以及释放动态内存空间)

    文章目录 前言 malloc函数 calloc函数 realloc函数 free函数 - 避免内存泄漏 常见的动态内存错误 前言 如果我们被问道:如何创建一个可以根据用户需求来开辟大小的数组? 可能有 ...

  3. C语言之动态内存管理

    目录 为什么存在动态内存管理 动态内存函数 malloc和free函数 calloc realloc 动态内存的常见错误 1对空指针的解引用操作 2对动态开辟空间的越界访问 3使用free来释放非动态 ...

  4. C语言第十三课,动态内存分配

    动态内存分配的空间放在堆区.动态内存函数主要有:malloc,calloc,realloc 动态内存函数的介绍 malloc 申请一个空间,大小是size的大小,指向的一个类型不明,因为在设计的时候, ...

  5. 【带你吃透C++】C++动态内存管理

    C++动态内存管理 前言 1. C/C++内存分布 例题理解 2.C语言中动态内存管理方式( malloc/calloc/realloc和free) 3.C++中动态内存管理方式 new和delete ...

  6. C语言动态内存开辟详解(malloc,calloc,realloc,free,柔型数组)

    目录 一.概述 二.相关库函数的使用 1.malloc 2.calloc malloc vs. calloc 异同 3.free的使用 4.realloc 三.易错点 四.C\C++程序的内存开辟规则 ...

  7. C语言-动态内存管理(malloc()、calloc()、realloc()、free())

    C语言 动态内存分配 文章目录 C语言 动态内存分配 前言 一.为什么存在动态内存分配? 二.动态内存函数的介绍 1.初识malloc()和free() 2.malloc()和free()的简单使用 ...

  8. 动态内存管理 - malloc、calloc、realloc、柔性数组

    目录 一.为什么存在动态内存分配 二.动态内存函数的介绍 1.1 malloc 1.2 free 1) 动态开辟多少个字节的内存空间,返回该空间的起始地址:且开辟的空间使用方法,类似于数组,是一块连续 ...

  9. 动态内存分布——malloc,calloc,realloc,free的使用。以及关于动态内存的常见错误。

    我们知道内存的使用方式,可以在栈区,静态区,堆区,申请空间来储存变量. 但是他们这些内存区所存储的东西是不一样的. 局部变量 函数的形式参数 栈区 动态内存分配申请的空间 malloc,calloc, ...

最新文章

  1. centos7下安装pip以及mysql等软件
  2. 代码检查规则:Python语言案例详解
  3. ASP.NET2.0 验证cookie详解
  4. npm 有用的一些全局包
  5. hibernate相关知识
  6. 尚硅谷官网MySQL笔记
  7. ubuntu cmakelists 配置海康 SDK
  8. 简单的Qt倒计时程序--番茄钟
  9. android jni stl,Android NDK中C++ STL库动态和静态链接
  10. 使用MaxScale实现mysql读写分离
  11. mysql 分页 count 查询效率低下分析
  12. android aso优化工具,如何使用ASO优化工具优化安卓应用商店
  13. Linux下安装ODI
  14. HANA掀起数据处理狂潮 农夫山泉有点甜?
  15. Java之spilt()函数,trim()函数
  16. 数据加密-国密SM2对数据进行加解密
  17. Kanzi入门学习(二)
  18. 计算机用户怎么配置漫游,Windows下漫游用户配置和强制漫游用户配置详细过程...
  19. pytorch载入部分预训练权重
  20. onlyoffice 收费不_OMG!你家小区物业有没有这些乱收费的现象……|物业|物业管理|物业服务|门禁卡|停车费...

热门文章

  1. 2012年第三届C/C++ B组蓝桥杯省赛真题
  2. 专用解决内存不能read问题的软件
  3. 三维地图前端arcgis_【ArcGIS JS API + eCharts系列】实现二、三维网络路径图的绘制...
  4. 计算机合并单元格怎么操作,如何在Excel中批量合并单元格(快速一步)
  5. yj.iOS 仿微信长按摄像点击拍照
  6. 校尉羽书飞瀚海,顺序表中增删改
  7. Seastar源码阅读(三)future
  8. 深入场景,智能决策倍增数字化转型价值 | 爱分析报告
  9. 儿童护眼灯怎么选?2022年好用的儿童护眼台灯推荐
  10. 华为设备在路由引入时应用路由策略