释放p2指向内存释放专家使用语句是

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
* 每天都修炼一下英语流利说
* 不要让注意力局限在技术细节上
* 耐心地接受外界建设性的信息
* 注意休息,保重身体
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
2、内存的分配(内存的申请)
1)申请方式
¨Stack(静态分配):静态对象是有名字的变量,可以直接对其进行操作。由系统自动分配内存。 例如,声明在函数中一个局部变量 系统自动在栈中为b开辟空间。
¨Heap(动态分配):动态对象是没有名字的变量,需要通过指针间接地对它进行操作。需要程序员自己申请内存,并指明大小。
在C中,如p1 = (char *)malloc(10);
在C++中用new运算符,如p2 = new char[20]; &&//(char *)malloc(20);
分配堆空间之后,p1、p2得到所分配堆空间首地址,将会指向堆空间。
2)申请后系统的响应
¨栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出
¨堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
其次,大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
此外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
3) 申请大小的限制
栈:在Windows下,栈是高地址向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的。
在WINDOWS下,栈的大小是2M(也有的说是1M,总之它是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是低地址向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。
堆的大小受限于计算机系统中有效的虚拟内存,由此可见,堆获得的空间很灵活。
但由程序员操作的过程中容易发生内存泄露,也极易产生内存空间的不连续,即内存碎片(频繁使用malloc和free(new和delete)的结果)。
4)申请效率的比较
¨栈由系统自动分配,速度较快,但程序员是无法控制的。
¨堆是由malloc或者new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
5) 分配效率的比较
&&&&&& 栈是机器提供的数据结构,机器会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令。
堆则是C函数库提供的,它的机制很复杂。首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
此外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
可见,堆得存取效率和栈比较起来要低得多。
3、下面再给出一个数据存储区域的实例
#include &stdio.h&
#include &malloc.h&
#include &unistd.h&
#include &alloca.h&
extern void afunc(void);& //声明afunc()函数
extern etext, edata, &//声明三个外部变量
int bss_ // 未初始化全局数据存储在BSS区
int data_var = 42; // 初始化全局数据存储在数据区
// 定义了一个宏,用于打印地址
#define SHW_ADR(ID, I) printf("the %8s\t is at adr:%8x\n", ID, &I);
int main(int argc, char *argv[])
&&&&&& char *p, *b, *
&&&&&& printf("Adr etext:%8x\t Adr edata%8x\t Adr end %8x\t\n", &etext, &edata,&end);
&&&&&& printf("\ntext Location:\n");
&&&&&& SHW_ADR("main", main);& // 查看代码段main函数位置
&&&&&& SHW_ADR("afunc", afunc); &// 查看代码段afunc函数位置
&&&&&& printf("\NBSS Location:\n");
&&&&&& SHW_ADR("bss_var", bss_var);& // 查看BSS段变量位置
&&&&&& printf("\NDATA Location:\n");
&&&&&& SHW_ADR("data_var", data_var);& // 查看数据段变量位置
&&&&&& printf("\nSTACK Location:\n");
&&&&&& afunc();
&&&&&& p = (char *)alloca(32);&& &// 从栈中分配空间
&&&&&& if(p != NULL){
&&&&&&&&&&&&& SHW_ADR("start", p);& &// 打印栈空间的起始位置
&&&&&&&&&&&&& SHW_ADR("end", p+31); // 打印栈空间的结束位置
&&&&&& b = (char *)malloc(32*sizeof(char)); // 从堆中分配空间
&&&&&& nb = (char *)malloc(16*sizeof(char)); // 从堆中分配空间
&&&&&& printf("\NHEAP Location:\n");
&&&&&& printf("the heap start: %p\n", b); // 打印堆起始位置
&&&&&& printf("the heap end: %p\n", (nb+16*sizeof(char))); &// 打印堆结束位置
&&&&&& printf("\nb and nb in Stack\n");
&&&&&& SHW_ADR("b", b);&&&&& &&& // 打印栈中字符指针变量b的存储位置
&&&&&& SHW_ADR("nb", nb);& // 打印栈中字符指针变量nb的存储位置
&&&&&& free(b);& // 释放申请的堆空间
&&&&&& free(nb); &// 释放申请的堆空间
void afunc(void)
&&&&&& static int long level = 0; // 静态数据存储在数据段中
&&&&&& int stack_& // 局部变量,存储在栈区
&&&&&& if(++level == 5){
&&&&&&&&&&&&&
&&&&&& printf("stack_var is at:%p\n", &stack_var); // 打印局部变量的地址
&&&&&& afunc(); // 递归执行afunc函数
4、介绍几个内存管理函数
1)Malloc/free函数
extern void *malloc(size_t &num_bytes);
指向被分配内存首地址的指针,否则返回空指针NULL
注意:不能用free()来释放非malloc(), calloc(), realloc()函数所创建的堆空间,否则会发生错误。
2)new/delete(在C++中)
&&&&&& 使用new/delete运算符实现内存管理比malloc/free函数更有优越性。它们的定义如下:
Static void* operator new(size_t sz);
Static void* operator delete(void* p);
先看一段C++代码:
void test(void)
//申请一个sizeof(obj)大小的一块动态内存,并把头指针赋值给obj类型的指针变量a
&&&&&& obj *a =
//清除并且释放所申请的内存
下面通过一段代码具体介绍一下new/delete的用法:
&&&&&& Public:
&&&&&&&&&&&&& A() { count && “A is here!” && }&& //构造函数
&&&&&&&&&&&&& ~A() { count && “A is here!” && }& //析构函数
&&&&&& Private:
&&&&&&&&&&&&& Int I;
A* pA = new A;&& // 调用new运算符申请空间
delete pA;&&&& // 删除pA
&&&&&& 其中,语句new A完成了一下两个功能:
A.调用new运算符,在堆上分配一个sizeof(A)大小的内存空间。
B.调用构造函数A(),在所分配的内存空间上初始化对象。
语句delete pA完成的是相反的两件事:
A.调用析构函数~A(),销毁对象。
B.调用运算符delete,释放内存。
¨使用new比使用malloc()有以下优点
A.New 自动计算要分配给对象的内存空间大小,不使用sizeof运算符,这样一来简单,而来可以避免错误。
B.自动地返回正确的指针类型,不用进行强制类型转换。
C.用构造函数给分配的对象进行初始化。
¨使用malloc函数和new分配内存的时候,本身并没有对这块内存空间做清零等任何工作。因此,申请内存空间后,其返回的新分配的空间是没有用零填充的,程序员须使用memset()函数来初始化内存。
3)realloc函数(更改已经配置的内存空间)
函数定义:
&&&&&& void *realloc(void *ptr, size_t size)
参数ptr为先前由malloc、calloc和realloc所返回的内存指针,而参数size为新配置的内存大小。
&&&&&& realloc函数用来从堆上分配内存,当需要扩大一块内存空间时,realloc()试图直接从堆上当前内存段后面的字节中获得更多的内存空间:
¨如果能够分配成功,则返回指向这块新内存空间的首地址,而将原来的指针(realloc函数的参数指针)指向的空间释放掉;
¨如果当前内存段后面的空闲字节不够,那么就使用堆上第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,而将原来的数据块释放掉;
¨如果内存不足,重新申请空间失败,则返回NULL,此时原来的指针(realloc函数的参数指针)仍有效。
#include &stdio.h&
#include &stdlib.h&
int main (int argc, char* argv[], char* envp[])&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& //主函数
& int *numbers1;
& int *numbers2;
& numbers1 = NULL;
& if((numbers2 = (int *)malloc(5*sizeof(int))) == NULL)&&&&&&&&& //numbers2指针申请空间
printf("malloc memory unsuccessful");
//free(numbers2);
//numbers2=NULL;
& for (n=0; n&5; n++) &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& //初始化(0-4)
&&&&& *(numbers2+n)=n;
&&&&& printf("numbers2's data: %d\n", *(numbers2+n));&&&& //把0-4打印出来
& printf("Enter an integer value you want to remalloc ( enter 0 to stop)\n"); //新申请空间大小 
& scanf ("%d", &input);
& numbers1 = (int *)realloc(numbers2, (input+5)*sizeof(int));&&&&&&&&&&&&&&&&&&&& // 重新申请空间
& if (numbers1 == NULL)
&&&&&&&& printf("Error (re)allocating memory");
&&&&&&&& exit (1);
for(n=0;n&5;n++)&&&&&&&&&&&&&&&&&&&&&&&&& // 这5个数是从numbers2(原指针空间)拷贝而来
printf("the numbers1s's data copy from numbers2: %d\n", *(numbers1+n));
&for(n=0; n& n++) &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& // 新数据初始化(0-input)
&&&&& *(numbers1+5+n) = n*2;
&&&&& printf ("nummber1's new data: %d\n", *(numbers1+5+n));& // numbers1++;
& printf("\n");
&&free(numbers1);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& // 释放numbers1
& numbers1 = NULL;
//& free(numbers2);&&&&&&&&&&&&&&&& // 不能再释放numbers2,因为number2早已被系统自动释放
& return 0;
本文大部分内容来自 《Linux高级程序设计》 杨宗德
有一部分是自己的总结
阅读(890)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_094071',
blogTitle:'Linux内存管理的相关知识',
blogAbstract:'1、C程序结构
C程序在没有调入内存之前(也就是在存储时),分为代码区(text)、数据区(data)和未初始化数据区(bss)3个部分。
代码区 存放CPU执行的机器指令,即函数体的二进制代码。由于对于频繁被执行的程序,只需要在内存中有一份代码即可,所以代码区是可共享的(可以被别的程序调用)。为了防止程序意外地修改代码区的机器指令,通常代码区是只读的。
全局初始化数据区和静态数据区 包含已经被初始化的全局变量、静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。
未初始化数据区 存储的是全局未初始化变量。
以上谈的是存储时C语言的程序结构,下面看看运行时C语言的程序结构。
代码区 该区的机器指令根据程序设计流程依',
blogTag:'linux内存管理的相关知识',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:0,
permalink:'blog/static/',
commentCount:1,
mainCommentCount:1,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'* 每天都修炼一下英语流利说\n* 不要让注意力局限在技术细节上\n* 耐心地接受外界建设性的信息\n* 注意休息,保重身体',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}

我要回帖

更多关于 释放内存 的文章

 

随机推荐