c语言 malloc calloc申请堆内存 后, 如果没有free 会怎么样?? 这块内存以后 还可以用吗??

内存管理(2)
1. malloc()函数
1.1 malloc的全称是memory allocation,中文叫动态内存分配。
原型:extern void *malloc(unsigned int num_bytes);&
说明:分配长度为num_bytes字节的内存块。如果分配成功则返回指向被分配内存的指针,分配失败返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。
1.2 void *malloc(int size);&
说明:malloc 向系统申请分配指定size个字节的内存空间,返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。   
备注:void* 表示未确定类型的指针,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是char还是int或者...)
void free(void *FirstByte): 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
1.4注意事项
1)申请了内存空间后,必须检查是否分配成功。
2)当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。&
3)这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
4)虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一些编译器的检查。
1.5& malloc()到底从哪里得到了内存空间?
答案是从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
2. new运算符
2.1 C++中,用new和delete动态创建和释放数组或单个对象。
动态创建对象时,只需指定其数据类型,而不必为该对象命名,new表达式返回指向该新创建对象的指针,我们可以通过指针来访问此对象。
这个new表达式在堆区中分配创建了一个整型对象,并返回此对象的地址,并用该地址初始化指针pi 。
2.2&动态创建对象的初始化
动态创建的对象可以用初始化变量的方式初始化。
int *pi=new int(100); //指针pi所指向的对象初始化为100
string *ps=new string(10,’9’);//*ps 为“”
如果不提供显示初始化,对于类类型,用该类的默认构造函数初始化;而内置类型的对象则无初始化。
也可以对动态创建的对象做值初始化:
int *pi=new int( );//初始化为0
int *pi=//pi 指向一个没有初始化的int
string *ps=new string( );//初始化为空字符串 (对于提供了默认构造函数的类类型,没有必要对其对象进行值初始化)
2.3&撤销动态创建的对象
delete表达式释放指针指向的地址空间。
// 释放单个对象
delete [ ]//释放数组
如果指针指向的不是new分配的内存地址,则使用delete是不合法的。
2.4&在delete之后,重设指针的值
//执行完该语句后,p变成了不确定的指针,在很多机器上,尽管p值没有明确定义,但仍然存放了它之前所指对象的地址,然后p所指向的内存已经被释放了,所以p不再有效。此时,该指针变成了悬垂指针(悬垂指针指向曾经存放对象的内存,但该对象已经不存在了)。悬垂指针往往导致程序错误,而且很难检测出来。
一旦删除了指针所指的对象,立即将指针置为0,这样就非常清楚的指明指针不再指向任何对象。(零值指针:int *ip=0;)
2.5 区分零值指针和NULL指针
零值指针,是值是0的指针,可以是任何一种指针类型,可以是通用变体类型void*也可以是char*,int*等等。
空指针,其实空指针只是一种编程概念,就如一个容器可能有空和非空两种基本状态,而在非空时可能里面存储了一个数值是0,因此空指针是人为认为的指针不提供任何地址讯息。参考:
2.6&new分配失败时,返回什么?
1993年前,c++一直要求在内存分配失败时operator&& new要返回0,现在则是要求operator&& new抛出std::bad_alloc异常。很多c++程序是在编译器开始支持新规范前写的。c++标准委员会不想放弃那些已有的遵循返回0规范的代码,所以他们提供了另外形式的operator&& new(以及operator&& new[])以继续提供返回0功能。这些形式被称为“无抛出”,因为他们没用过一个throw,而是在使用new的入口点采用了nothrow对象:&
class&& widget&& {&& ...&& };
widget&& *pw1&& =&& new&&//&& 分配失败抛出std::bad_alloc&&
if&& (pw1&& ==&& 0)&& ... //&& 这个检查一定失败
widget&& *pw2&& =&& new&& (nothrow)&&&& //&& 若分配失败返回0
if&& (pw2&& ==&& 0)&& ... //&& 这个检查可能会成功
3. malloc和new的区别
3.1&new 返回指定类型的指针,并且可以自动计算所需要大小。
比如:   
1) int *p;   
p = //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int);   
parr = new int [100]; //返回类型为 int* 类型(整数型指针),分配大小为 sizeof(int) * 100;   
2) 而&malloc 则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针。&  
p = (int *) malloc (sizeof(int)*128);//分配128个(可根据实际需要替换该数值)整型存储单元,并将这128个连续的整型存储单元的首地址存储到指针变量p中&&
double *pd=(double *) malloc (sizeof(double)*12);//分配12个double型存储单元,并将首地址存储到指针变量pd中
3.2&malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。
除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。
4.有了malloc/free为什么还要new/delete?
1) malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
2) 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。
3) 既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,结果也会导致程序出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:5538次
排名:千里之外
原创:24篇
(3)(21)(6)(3)问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
1.free 一段内存后,为什么还可以对这段内存进行读写。。。按照网上以及书上的说法,释放内存后,这段内存就不应该使用了,操作系统就可以分配给其他任务。。
我的疑问是,释放内存后,这段内存资源还是否属于当前进程???
如果属于当前进程,那么读写访问无可厚非。
可是,操作系统可以再次利用这段内存并分配给其他人,那么这里的 “其他”只是限定在当前进程中吗,只是给当前进程其他代码中的内存申请来使用么?
2.局部的数组,在函数运行完毕后,应该释放,但是为什么依然可以读写其中的数据
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
这是一个很复杂的问题,我谈谈自己的理解。
首先你需要知道,进程使用的都是虚拟地址空间,每个进程都有独立的,完整的4GB(32bit下)地址空间,未必每一块内存都会映射到物理内存,这个映射工作是操作系统完成的。如果你访问了地址空间里未映射的内存,或者写了只读的区域,操作系统就会报错(Segment Fault错误,呵呵~)并终止你的程序。
当一个进程开始运行时,会向操作系统申请一块“堆”内存,由程序自己对这块堆内存进行管理,malloc就是从这块内存中分配内存,在C语言中,这个申请和管理堆内存的工作是由运行库自动完成的。
当free一块内存后,free(即运行库)会将这块内存标记为未使用,下次有可能会将这块内存分配出去。但这块内存对进程来说仍然是可以读写的,因为运行库已经向操作系统申请,自己来管理这块内存了。
局部变量是分配在栈上的,进程开始运行时,操作系统会分配给进程一块固定大小(通常是1MB)的栈,所谓分配和释放局部变量,都只是在移动栈顶指针而已,只要没超过栈的这1MB内存区域,都还是可以读写的。
以上都是常见操作系统的典型行为,也许在某些操作系统和平台上并非这么工作,总之,使用已释放的内存是非常危险的行为。
有兴趣可以读这两本书《深入理解计算机系统》《链接装载与库》
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
十几年前上学的时候,计算机还是紧缺资源,在没有购买个人电脑之前,我们通常会去学校的计算机室上机。计算机室有专人管理,规矩诸多,其中包括“不要随意修改系统配置文件”,“不要做和学习无关的事情”等等。而我们经常会很开心地先把autoexec.bat/config.sys大改一气(很没出息是吧,不久几十K字节的事儿么,:)),然后再往机器上拷贝个金庸群侠传之类的,偷偷玩上半个小时。第二天再去上机的时候,直奔昨天保存文件的目录。运气好的时候,文件还在,于是大喜,接着进度继续玩;运气不好的时候,不但文件已经被删,而且还发现机器上新增的病毒好厉害,->_->。
说这个陈芝麻烂谷子,也许你已经明白我的意思。
其实我想说的就是两点:
1. 你可以不遵守规则,但不等于没有规则。
2. 不遵守规则而产生的后果是不可预测的(undefined)。
楼主没有明确说明为什么会认为free的内存仍然可用,以及为什么认为局部变量的数据仍然可用。
为了说明问题,我假设以下的程序:
#include&stdio.h&
#include &stdlib.h&
int *lvret(void) {
int ret = 5;
int main(void) {
int *p = lvret();
printf("%d\n",*p);
编译运行这个程序的结果很可能是5。
所以函数结束后,局部变量的数据仍然可用是吗?
再来考虑下面这个程序:
#include&stdio.h&
#include &stdlib.h&
int *lvret(void) {
int ret = 5;
void mod(void) {
int a = 7;
int main(void) {
int *p = lvret();
printf("%d\n",*p);
这个程序运行的结果很可能是7。
显然,这个内存地址现在变得不那么可靠了。
(编译器还是会给出警告,比如gcc 4.8)
warning: function returns address of local variable [-Wreturn-local-addr]
所以这个例子告诉我们,你能够访问到的内存空间并不总是安全的。
换句话说,你发现释放后的内存数据或者局部变量占用的内存仍然可以读写,只不过是偶然的情况 -- 刚好没有被别的程序动过而已。
C语言并不是一个内存安全的语言。
C++也不是,但C++11已经好很多了(接纳了smart pointer)。
以上主要说明楼主描述的操作为什么从根本上是需要避免的。
再来补充回答一下楼主的疑问:
是Heap(堆)的管理。
楼主的潜台词应该是“既然内存释放了,那么在访问的时候为什么不出现segmentation fault”。
回答是 -- 这是C运行库实施层面的问题。大多数运行库的实施不会试图去识别那些已经被"free"的内存块,并把它们退回系统(所谓退回系统,就是取消在进程地址空间上的映射)。因此,在访问这些地址的时候,segmentation fault没有如预料中出现。
但并不全是这样,也有例外,比如说OpenBSD就是一个。访问,你可以看到如下描述:
On a call to free, memory is released and unmapped from the process address space using munmap. This system is designed to improve security by taking advantage of the address space layout randomization and gap page features implemented as part of OpenBSD's mmap system call, and to detect use-after-free bugs—as a large memory allocation is completely unmapped after it is freed, further use causes a segmentation fault and termination of the program.
这也从侧面证明了楼主观察到的现象是不可靠的。
(至于为什么多数运行库要采取这样的内存管理策略,又是另一个话题了)
是Stack(栈)的管理。
@精英王子 已经说明了。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
内存管理有以下几个层次(从高到低):C程序 - C库(malloc)- 操作系统 - 物理内存
首先,操作系统保证每个进程都有独立的虚拟内存空间(32bit上应该是4G吧,一般进程也用不了这么多)。当然实际上物理内存是所有进程共享的,所以当你需要动态内存时,需要向操作系统申请,这时候虽然从你程序的角度,内存是连续的,其实是被操作系统映射到某一块物理内存而已。程序用完内存归还后,实际归还的部分可能被操作系统分配给其他进程。
要注意,上面说的“归还”是malloc库的行为。malloc库会使用一些策略来提高内存使用的效率,比如程序需要使用10K内存时,malloc实际可能上会申请1M,因为一次系统调用开销很大;再比如即使你调用了free“归还“了程序使用的内存,malloc库也可能并未真正把这些内存归还给操作系统,因为将来程序可能还会再申请动态内存。
malloc库有多种实现,我知道的一种是使用标记(tag)来存储内存的元信息。比如你申请了8个byte,得到的头指针地址是0x1001(实际内存为0x8),malloc会在0x1000(也就是头指针-1的位置)保存8,即这段内存的长度。等释放时,程序将头指针地址传给free,malloc库从头指针-1的位置发现需要释放的内存长度,释放内存(实际的操作可能只是将tag清空)。这就解释了:1. 为什么和malloc不同,free的参数只有一个头指针而不需要长度;2. free后内存实际上可能并未归还给操作系统。
所以,访问被(程序)释放的内存是一种undefined行为,就是说结果是不确定的。在malloc库未将此内存归还给操作系统也未进行下一次动态分配时,这块内存事实上仍属于程序。而当malloc库不清理归还的内存时(多数实现都是如此),你能访问到的值仍是原来的值。这和函数调用完毕而未清理栈帧、后续调用函数可以访问到之前已经设置的局部变量值是一个道理。
但是,当malloc库已经将内存归还给系统时,再去访问原来的地址(更别说写),由于这段地址已经不属于程序了,就会出现经典的segmentation fault。
说到底,这些现象还是C语言库为了更有效率的实现而妥协的结果。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
ls 说的很详细了,在释放内存时一般建议把指针置空,这样来避免使用到已释放的内存。如:
pa = NULL;
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
free()和malloc()用一些数据结构(主要是链表)管理从堆中分配的内存,但它们只是库函数,真正改变堆边界的系统调用是sbrk()。malloc()发现当前内存不足够分配时,会先调用sbrk()扩大堆的边界。
free()释放内存实际是数据结构中把该部分标记为未使用,但它实际还在堆中,所以还能用。当有大量堆内存都没有使用,大部分的实现会收缩边界,这时,释放掉的内存就不能用了。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
Linux下Glibc的内存管理机制大致如下:
从操作系统的角度看,进程的内存分配由两个系统调用完成:brk和mmap。brk是将数据段(.data)的最高地址指针edata往高地址推,mmap是在进程的虚拟地址空间中找一块空闲的。其中,mmap分配的内存由munmap释放,内存释放时将立即归还操作系统;而brk分配的内存需要等到高地址内存释放以后才能释放。也就是说,如果先后通过brk申请了A和B两块内存,在B释放之前,A是不可能释放的,仍然被进程占用,通过TOP查看疑似”内存泄露”。默认情况下,大于等于128KB的内存分配会调用mmap/mummap,小于128KB的内存请求调用sbrk(可以通过设置MMMAP_THRESHOLD来调整)。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
去看这个,有快捷导航小标题的,只看9.9节即可,看完,你就知道malloc怎么管理虚拟内存了,god bless you.
同步到新浪微博
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
扫扫下载 App
SegmentFault
一起探索更多未知【求助】使用malloc后不用free没事,用free后会出错_c语言吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:470,026贴子:
【求助】使用malloc后不用free没事,用free后会出错收藏
运行到free那里的时候会出现这个窗口
c语言海同强大的师资阵容,因人制定课程内容,分阶段学习.c语言就到正规IT技术培训机构-海同科技,培训IT技术面对面教学,免费重读!
你动过指针?
#include "stdio.h"#include "stdlib.h"int seek(char str[]);int main(int argc, char* argv[]){char *str=(char*)malloc(sizeof(char));printf("请输入一个字符串:");gets(str);if(seek(str))printf("其中最长的单词是第%d个单词.\n",seek(str));elseprintf("字符串只有一个单词或未输入.\n");free(str);return 0;}int seek(char *str){int i=0,num=0,tem=0,position=0,length=0;while(str[i]!='\0'){if(str[i]!=' '){num++;}else{
tem++;if(num&length){length=position=num=0;}}i++;}}
好像是因为使用的空间大于申请的空间?
我感觉吧,可能是free的参数问题吧,应该是void类型,str是char类型。
我在Ubunut下使用gcc 4.8对你的代码编译并且运行了。测试结果表明:即使你只是申请了一个字节的内存,但是你人然可以写入类似:"Hello world"这样明显多个字节的字符串到str所指向的内存空间,且释放内存也没有错误。其实这也表明了一个问题,即,malloc()函数在向系统申请存储空间时,会使用一些策略,也就是说有可能只是申请了一个字节,但实际可供使用的内存空间可能会是1000个字节。为什么这样呢?因为,一次系统调用的开销是很大的,所以,尽力多分配一点,以防后面你可能还要接着申请。此外,free()之后,原来的内存块理论上仍然可以访问,只要它没有被别的程序申请内存时占用。实际上free()之后的那个内存块短时间内依然不会被系统回收,而是防止此前应用程序依然需要申请内存时使用。以上是个人见解,如若有误还望见谅。
图为测试结果。不过貌似你的测试函数有问题,每次都说第一个单词最长。。。
C++培训---美国上市C++培训公司,入学签订就业协议,名企疯抢达内C++学员.达内C++培训全程以&实战&教学,&90天=2年工作经验& 0元0基础 + 7天免费试学!
100%是堆溢出,自己检查一切内存复制、字符串拷贝行为……
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或C语言中使用malloc分配内存后没有回收,有办法解决么?_百度知道C/C++(1)
JobHunting(16)
1. 这道题是15年鹅厂研发类实习生的笔试题,我不知道答案。
2. 知乎上有人问同样的问题,在此贴出来学习。
我对malloc和free的理解:
malloc时,假如申请了1KB的空间,那么操作系统有可能给你一个大于1KB的数值用以备用。同时,malloc时会用一个tag记录本次申请空间的大小,free的时候仅仅将tag清零。
但我有两个问题,比如在基于glibc的Linux中:
1.malloc之后如果不free,那么进程正常(或异常)结束后,操作系统是否一定会对这块内存进行回收呢?
2.malloc之后如果调用了free,那么有没有什么情况下,操作系统不对这块内存进行回收呢?或者说暂时不进行回收呢?
回答者(得分最高):
会的,内存也是资源,操作系统会回收的。若不回收,你每次都异常退出去,多来几次,那岂不是你的内存直接就没了。
2. 若是glibc,你所free掉的内存,不一定会马上被OS回收,这是合理的。
试想一下,你每次free掉的内存都还给OS的话,尤其是在小字节的情况下,那么造成的情况,就是一大块的内存被你弄的千疮百孔,也就是说一块内存,里面有很多gap。而在操作系统的虚拟内存管理中,更是管理着的是固定大小的内存,如4K,那你还给我1
Byte,OS显然是很尴尬的。
于是为了避免这样的问题,那么内存管理一般会有一个free
block list,free掉的东西就放在这里来。那么你可能会释放很散乱的内存过来,没关系,我们在这里会尝试合并这些散乱的block,而malloc首先找的也是free block list,而非从OS申请新的内存。那么此时如果找到了一块儿合适的自然最好,如果找到的是比要的更大,那么一部分malloc,另一部分放回去。而上面有同学提到了小内存的问题,而这也是free block list在头部会有一些所谓的administrative data,所以用标准的malloc和free管理小内存是不高效,因为越小越容易造成gap。当然,由于malloc和free是如此普遍,自然会尝试着让它变的更好,所以也有各种优化,如对free
block list进行chunk size排序等,不过这里就不提了,再谈下去,问题就很大了,这块儿也是有专门的Memory Management书籍讨论的。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:19189次
排名:千里之外
原创:45篇
转载:77篇
(1)(16)(6)(3)(6)(11)(2)(9)(6)(5)(1)(5)(2)(1)(4)(25)(16)

我要回帖

更多关于 c语言malloc函数 的文章

 

随机推荐