ImageVerifierCode 换一换
格式:DOCX , 页数:77 ,大小:236.52KB ,
资源ID:12005222      下载积分:10 金币
快捷注册下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

开通VIP
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.zixin.com.cn/docdown/12005222.html】到电脑端继续下载(重复下载【60天内】不扣币)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

开通VIP折扣优惠下载文档

            查看会员权益                  [ 下载后找不到文档?]

填表反馈(24小时):  下载求助     关注领币    退款申请

开具发票请登录PC端进行申请

   平台协调中心        【在线客服】        免费申请共赢上传

权利声明

1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

注意事项

本文(C语言内存管理及经典算法.docx)为本站上传会员【仙人****88】主动上传,咨信网仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知咨信网(发送邮件至1219186828@qq.com、拔打电话4009-655-100或【 微信客服】、【 QQ客服】),核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载【60天内】不扣币。 服务填表

C语言内存管理及经典算法.docx

1、C语言内存管理 对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题,为了应对这个问题,有许多技术被研究出来来解决这个问题,例如Smart Pointer,Garbage Collection等。一般我们常说的内存泄漏是指堆内存的泄漏。那么为什么会导致内存泄漏呢?通过学习内存管理,相信你一定能解决好这个问题。 1-1 C语言内存管理方式 在进入本专题前,我们先看一下下面的程序,来简单分析以下C语言的内存管理: #include #include //全局变量定义 int iGlobalInt1=0; int

2、 iGlobalInt2=0; int iGlobalInt3=0; //全局常量定义 const int iGlobalConstInt1=1; const int iGlobalConstInt2=5; const int iGlobalConstInt3=6; //全局静态变量定义 static int iGlobalStaticInt1=0; static int iGlobalStaticInt2=0; static int iGlobalStaticInt3=0; //函数定义 void funcParamTest(int iFuncParam1

3、int iFuncParam2,int iFuncParam3) { //函数私有变量定义 int iLocalInt1=iFuncParam1; int iLocalInt2=iFuncParam2; int iLocalInt3=iFuncParam3; printf("函数参数变量内存地址\n"); printf("iFuncParam1=0x%08x\n",&iFuncParam1); printf("iFuncParam2=0x%08x\n",&iFuncParam2); printf("iFuncParam

4、3=0x%08x\n\n",&iFuncParam3); printf("函数本地变量的内存地址\n"); printf("iLocalInt1=0x%08x\n",&iLocalInt1); printf("iLocalInt2=0x%08x\n",&iLocalInt2); printf("iLocalInt3=0x%08x\n\n",&iLocalInt3); return; } //入口函数 int main(int argc, char* argv[]) { //局部静态变量 static int iStaticInt1=

5、0; static int iStaticInt2=0; static int iStaticInt3=0; //局部静态常量定义 const static int iConstStaticInt1=0; const static int iConstStaticInt2=0; const static int iConstStaticInt3=0; //局部常量 const int iConstInt1=1; const int iConstInt2=5; const int iConstInt3=6;

6、 //局部变量 int iLocalInt1=0; int iLocalInt2=0; int iLocalInt3=0; char * pMalloc1,*pMalloc2,*pMalloc3; char * pNew1,*pNew2,*pNew3; printf("全局常量的内存地址\n"); printf("iGlobalConstInt1=0x%08x\n",&iGlobalConstInt1); printf("iGlobalConstInt2=0x%08x\n",&iGlobalConstInt2);

7、 printf("iGlobalConstInt3=0x%08x\n\n",&iGlobalConstInt3); printf("iConstStaticInt1=0x%08x\n",&iConstStaticInt1); printf("iConstStaticInt2=0x%08x\n",&iConstStaticInt2); printf("iConstStaticInt3=0x%08x\n\n",&iConstStaticInt3); printf("全局变量的内存地址\n"); printf("iGlobalInt1=0x%08x\n",&i

8、GlobalInt1); printf("iGlobalInt2=0x%08x\n",&iGlobalInt2); printf("iGlobalInt3=0x%08x\n\n",&iGlobalInt3); printf("静态变量的内存地址\n"); printf("iGlobalStaticInt1=0x%08x\n",&iGlobalStaticInt1); printf("iGlobalStaticInt2=0x%08x\n",&iGlobalStaticInt2); printf("iGlobalStaticInt3=0x%08x\n\n",&i

9、GlobalStaticInt3); printf("iStaticInt1=0x%08x\n",&iStaticInt1); printf("iStaticInt2=0x%08x\n",&iStaticInt2); printf("iStaticInt3=0x%08x\n\n",&iStaticInt3); printf("本地变量的内存地址\n"); printf("iConstInt1=0x%08x\n",&iConstInt1); printf("iConstInt2=0x%08x\n",&iConstInt2); printf("iCons

10、tInt3=0x%08x\n\n",&iConstInt3); printf("iLocalInt1=0x%08x\n",&iLocalInt1); printf("iLocalInt2=0x%08x\n",&iLocalInt2); printf("iLocalInt3=0x%08x\n\n",&iLocalInt3); funcParamTest(iLocalInt1,iLocalInt2,iLocalInt3); //在堆上分配内存,使用new pNew1=new char[16]; pNew2=new char[16]; pNew3

11、new char[16]; //在堆上分配内存,使用malloc pMalloc1 = (char *)malloc( 16 ); pMalloc2 = (char *)malloc( 16 ); pMalloc3 = (char *)malloc( 16 ); printf("在堆上分配内存内存地址\n"); printf("pMalloc1=0x%08x\n",pMalloc1); printf("pMalloc2=0x%08x\n",pMalloc2); printf("pMalloc3=0x%08x\n\n",pMalloc3

12、); //释放new 分配的内存空间 delete [] pNew1; delete [] pNew2; delete [] pNew3; pNew1=NULL; pNew2=NULL; pNew3=NULL; //释放malloc分配的内存空间 free(pMalloc1); free(pMalloc2); free(pMalloc3); pMalloc1=NULL; pMalloc2=NULL; pMalloc3=NULL; return 0; } 本程序在Windows

13、 XP 下,VC6编译后的执行结果是: 注意,上面我们输出的完全是内存地址,也就是说,是程序在进程中内存地址(注意是虚拟内存地址而不是物理内存地址)。我们认真观察程序输出,发现每种类型的内存地址都是连续的,而不同类型之间内存地址有的是连续的,有的差别极大(注意:不同编译器可能输出的结果不一样,但这并不影响我们分析问题)。基本上,我们可以把这些地址范围分为如下几个部分:堆、栈、全局/静态存储区和常量存储区。 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动释放的存储区。里面的变量通常是局部变量、函数参数等。在栈上分配内存,通常是指在执行函数时,函数内局部变量在栈上创建,函数执

14、行结束时这被自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 堆,就是那些由new或使用malloc分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new/malloc就要对应一个delete/free。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。动态内存的生存期由我们决定,使用非常灵活,但问题最多,也是我们本章讨论的重点。 全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。静态存储区在程序编译的时候就已

15、经分配好,这块内存在程序的整个运行期间都存在。 常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(通过特殊手段当然是可以修改的,例如Windows下可直接修改PE文件)。 通过分析上面的程序,我们大抵可以绘出程序内存分配情况 … … 栈 … … 代码区 静态数据区 低端内存区域 高端内存区域 … … 常量存储区 堆 通过上面分析,我们知道,全局常量(例如iGlobalConstInt1)和局部静态常量(例如iConstStaticInt1)位于常量存储区;全局变量(例如iGlobalInt1)和局部静态变量(iStaticInt1)位

16、于静态数据区;本地变量(例如iLocalInt1)和函数参数变量(例如iFuncParam1)位于栈区,它是动态存储区的一部分;使用malloc/new(例如pMalloc1,pNew1)分配的内存位于堆区,它也是动态存储区的一部分。 由于常量存储区和静态数据区都是在程序编译的时候就分配好空间了,而堆栈是在程序运行过程中自动分配好的空间。使用堆分配内存是显式分配的,我们在稍后详细介绍。下面简单介绍一下使用栈分配内存空间的原理。 我们来看看我们DEMO程序对于函数参数那一部分,我们发现,栈分配如下所示 … … iLocalInt2 = 0x0012ff3c iLocal

17、Int3 = 0x0012ff40 RET iFuncParam2 = 0x0012ff4c 低端内存区域 高端内存区域 … … iFuncParam1 = 0x0012ff48 iFuncParam3 = 0x0012ff50 iLocalInt1 = 0x0012ff38 我们发现函数参数地址和变量地址分布如上,其中多了四个字节,正好就是RET指令。首先,三个参数以从又到左的次序压入堆栈,先压“iFuncParam3”,再压“iFuncParam2”,最后压入“iFuncParam1”;然后压入函数的返回地址(RET),接着跳转到函数地址接着执行。第三步,

18、将栈顶(ESP)减去一个数,为本地变量分配内存空间,接着就初始化本地变量的内存空间。感兴趣的读者可以使用工具反汇编上面的代码,然后就可以看到C语言是如何编译的了。 从上面我们可以看出,对于栈分配内存而言,是由编译器自动管理,无需我们手工控制。然而,对于堆而言,内存分配与释放由程序员控制了,方法非常灵活,但也最容易出现问题。 1-2 C语言内存管理 上一节中我们简单分析了C语言内存管理方式,其中对于堆的管理,我们仅进行了简单描述。在本节中我们要详细描述C语言内存管理。在介绍之前,我们需要先熟悉C语言堆内存管理涉及的函数。 alloca void * alloca( size_t

19、 size ); alloca的作用分配一块大小为size个字节的可用内存块,并返回首地址。不能分配的时候返回NULL。alloca是从栈中分配内存空间,使用alloca分配内存后不必使用free来释放内存。alloca是分配一块未经初始化的内存,这和 malloc 一样,如果需要初始化可以调用 memset 函数。在上节中,我们已经对栈分配做了详细的描述,栈的释放是由编译器自动管理的,所以不需要我们手动去释放它。 示范代码如下: #include #include int main() { int s

20、ize = 1000; int errcode = 0; void *pData = NULL; if (size > 0 && size < 1024) { pData = alloca( size ); if(pData != NULL) { printf_s( "Allocated %d bytes of stack at 0x%p",size, pData); }else{ printf_s( "Allocated %d bytes of stack failed"

21、size); } }else{ printf_s("Tried to allocate too many bytes.\n"); } } malloc 与 free void * malloc( size_t size ); void free( void *ptr ); malloc的作用分配一块大小为size个字节的可用内存块,并返回首地址。不能分配的时候返回NULL。malloc 是从堆中分配内存空间,使用malloc 分配内存后必须使用free释放内存。 free清除ptr所指向的地址,它只

22、作清除的工作,并告诉系统,这块地址已经被释放和清除,可以重新被分配。使用malloc 分配的内存没有进行初始化,也就是说,该内存区中可能存在先前内容,而calloc 则将内存初始化为0。如果需要对malloc 分配的内存初始化,可以使用memset 函数。 示范代码如下: #include /* For _MAX_PATH definition */ #include #include void main( void ) { char *string; /* Allocate

23、 space for a path name */ string = malloc( _MAX_PATH ); // In a C++ file, explicitly cast malloc's return. For example, // string = (char *)malloc( _MAX_PATH ); if( string == NULL ) printf( "Insufficient memory available\n" ); else { printf( "Memory space

24、allocated for path name\n" ); free( string ); printf( "Memory freed\n" ); } } calloc void * calloc( size_t nmemb,size_t size); calloc的作用是分配并初始化内存块,返回一个指向nmemb块数组的指针,每块大小为size个字节。它和malloc的主要不同之处是会初始化(清零)分配到的内存。 示范代码如下: #include #include void ma

25、in( void ) { long *buffer; buffer = (long *)calloc( 40, sizeof( long ) ); if( buffer != NULL ) printf( "Allocated 40 long integers\n" ); else printf( "Can't allocate memory\n" ); free( buffer ); } Realloc void * realloc( void *ptr, size_t size ); reallo

26、c以ptr所指地址为首址,分配size个字节的内存,并返回ptr所指地址。realloc不会初始化分配到的内存块,如果ptr为NULL则相当于malloc,如果size为NULL则相当于free(ptr)。不能分配返回NULL。 示范代码如下: #include #include #include void main( void ) { long *buffer; size_t size; if( (buffer = (long *)malloc( 1000 * sizeof( lo

27、ng ) )) == NULL ) exit( 1 ); size = _msize( buffer ); printf( "Size of block after malloc of 1000 longs: %u\n", size ); /* Reallocate and show new size: */ if( (buffer = realloc( buffer, size + (1000 * sizeof( long )) )) == NULL ) exit( 1 ); size = _m

28、size( buffer ); printf( "Size of block after realloc of 1000 more longs: %u\n", size ); free( buffer ); exit( 0 ); } 通过上面的学习,我们知道:alloca、calloc、malloc、realloc 负责分配内存,free 负责释放内存。其中 alloca 是在栈中分配内存,而calloc、malloc、realloc 是在堆中分配内存,也就是说 alloca 的内存分配,是有作用域的,不需要释放,而calloc、

29、malloc、realloc内存是没有作用域的,需要调用 free 主动释放分配内存区域。alloca,malloc,realloc只负责分配内存,并不初始化分配内存空间,而calloc不仅分配内存,还负责初始化分配内存为0。realloc 是以传入指针为基址,分配指定大小的内存区域。 当读者阅读到此时的时候,可能觉得内存管理其实很简单,无非是分配内存释放内存而已。大家不妨看看如下一个程序: void MyGetMemory(int iSize) { char * szTemp=(char *)malloc(iSize); if(!GetString(szTemp,iSize

30、)) { printf("getstring failed!\n"); return; } ... free(szTemp); } 相信大家能很快发现上面在GetString 函数返回失败的情况下,内存没有释放,将产生内存泄露。如果我们再更改一下,可能这个错误稍微隐蔽一点。 char * MyGetMemory(int iSize) { char * szTemp=(char *)malloc(iSize); if(!GetString(szTemp,iSize)) { printf("getstring failed!\n");

31、 return NULL; } return szTemp; } void Test() { char * szMalloc=MyGetMemory(23); if(szMalloc) { printf("out : %s \n",szMalloc); free(szMalloc); szMalloc=NULL; } } 这个程序的内存泄露同样是在GetString 失败的时候产生,我们单存分析Test 函数是发现不了内存泄露的。在实际项目中,由于较为复杂,可能忘记释放内容了,也有可能释放内容后再次释放内容等等,这些错误要么是程

32、序运行时间越久,所耗内存越大,要么直接出现异常。如果分配了内存忘记释放,那样就产生了内存泄漏。为了防止内存泄漏,一些项目甚至要求对分配、释放内存进行跟踪,以避免内存泄漏。最简单的方法就是封装内存分配和释放函数,实际分配中并不直接调用alloca、calloc、malloc、realloc来分配内存,也不直接调用函数free来释放内存。另外,在服务器上,由于程序需要长期执行,频繁的分配内存资源会导致内存碎片的增多,这样可以使用内存池来解决这些问题。 既然内存管理错误这么频繁,后果这么严重,那么作为一个新手程序应该如何来避免这些问题呢?在下一节我们将详细介绍。 1-3 C语言内存使用要点及常见

33、错误 在介绍内存使用要点之前,我们先看看使用C语言内存管理中经常出现的错误,尤其是新手。 1、 内存分配后没有校验,使得内存未成功,却正常使用。 2、 内存分配成功后,没有初始化就使用。 3、 内存分配成功,也进行了初始化,可是使用内存时出现了越界(缓冲区溢出)。这种错误如果被黑客成功利用,最严重的后果就是整个操作系统被黑客完全控制。 4、 内存分配成功后,忘记释放内存,产生内存泄漏。 5、 内存分配成功后,也正常释放内存,可是在释放内存后又继续使用。 6、 混淆指针和数组。 上面的这些问题,不仅仅是新手容易犯,一个工作多年的老程序员依然可能犯这样的错误。如果有一天,您发现

34、您的程序在debug下可以成功运行,可是在release下却不能成功运行,一种可能就是您的数据没有被初始化。如果有一天,您的程序出现一个访问一个非常内存地址的错误,那么你应该检查一下是否产生了越界错误等等。总而言之,上面的任何一种错误出现了,就极有可能不好定位错误,尤其是访问越界、释放后继续使用的错误。 内存分配后不校验直接使用主要是新手犯这种错误,由于没有意识到内存分配会不成功。这种问题的解决办法很简单,判断指针是否为空就可以了,在上节中的例子比比皆是,就不列举出来了。另外一种情况是函数的入参为空,可以使用assert(p!=NULL) 来简单检查,也可以使用if(p!=NULL) 来

35、判断。这类错误只要养成好习惯,是完全可以避免的。 内存分配后没有初始化,这种情况也是属于粗心引起,也通常是新手犯的错误。从上节中我们知道,alloca,malloc,realloc是只负责分配内存而不负责初始化内存的,完全可以想象,不初始化直接使用会导致不可预知的错误。其实,内存没有初始化是出现所有内存分配的情况下,可能是全局的,也有可能出现在栈上,当然更多是出现在堆分配上。比如如下的例子就是一个堆分配后没有初始化出现的错误,其实在实际项目中,肯定没有这么明显,中途隔了很多代码,所以往往不容易发现。 int iTimes; for(iTimes=0;iTimes<20;iTime

36、s++) { } While(iTimes>0) { … iTimes--; } 后来由于程序需要我们注释掉了那段for循环,结果变成了 int iTimes; /* for(iTimes=0;iTimes<20;iTimes++) { }*/ While(iTimes>0) { … iTimes--; } 当然上面的错误实在是太明显了。实际上这种例子主要是修改了程序的某个逻辑后才出现的,尤其是删出逻辑后。比如在c++类的构造删除中没有初始化成员指针变量,我们先前必须调用该类的某个函数来分配并初始化,可是后来我们去掉了这个函数,而我们在析构函数

37、中还保留着该指针的释放,这样当然会导致错误。总之,不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。 访问内存越界错误可以说是一类非常严重的错误,因为这种错误非常难找。黑客也通常利用这种漏洞来入侵系统。这种错误出现几率最常见的就是文件路径,我们可能想当然的认为文件名不会超过255,因为是我们自己的文件名,认为完全可以控制,可是由于种种原因我们的文件名大于了255,那么会产生什么样的错误呢?由于我们的文件名需要使用memcpy复制内容,那么有可能我们的一部份代码数据可能被覆盖了,会出现什么错误只有天知道,尤其是每次的执行流程未知,产生的后果每次都不一样,这样的错误通常是很

38、难定位的。鉴于此,微软在vc2005中增加了一系列_l的函数,例如_sprintf_l 增加了缓冲区长度,以减少这类问题。也就是说,对于程序员,无论是新手还是老手,在使用内存拷贝等情况下一定要考虑越界问题。 常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。 对于忘记释放内存的问题,也是一个比较热门的话题。出现这种情况的后果是,所占内存越来越大,系统越来越慢。尤其是对于服务器程序,由于需要

39、长时间运行,所以内存泄漏就变得非常重要了。所以我们在程序设计时一定要保证malloc/free、new/delete成对使用。这类问题还有可能出现在函数中分配内存的场合。例子如下: void getpathname(char * szPath) { if(szPath == NULL) { szPath=(char *)malloc(1024); } memcpy(szPath,”mycopybuffer”,11); } void callf() { char * szPath; szPath=NULL; getpathname

40、 (szPath); if(szPath != NULL) { Free(szPath); szPath=NULL; } } 对于上面这个例子,曾经有人还说过,我分配了,也释放了,可是就是有内存泄漏。不过更多的情况是发现数据不正确,还不知道怎么回事,其实就是对指针理解不够。对于上面的例子,解决办法通常有三个,一是直接返回指针,二是传入 char **,三是如果是c++可以传入 char * &。另外,需要避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。 释放后继续使用内存指针的问题也是一个非常严重的问题,如果加上逻辑复杂,测试不能

41、遍历所有路径的话,极有可能成为一个小BUG。那么出现这种问题有哪些情况呢? 1、 程序逻辑异常复杂,释放之后并没有将所有指向这块内存区域的指针清0。 2、 在函数中返回了栈内存的指针或引用。 3、 使用free或delete释放了内存后,没有将指针设置为NULL,导致产生“野指针”。还有就是存在多个这向该内存的指针,释放时没有将所有指向该内存地址的指针设置为NULL。 程序逻辑异常的例子不太好找,但最容易理解。避免这类情况的方法就是尽量避免多个指针同时指向一个内存地址空间。如果的确不可避免,一定要在释放内存后,将所有指向该地址的指针都设置成NULL。 函数种返回了栈内存地址或

42、引用这个例子比较容易理解。如下: char * GetTemp() { char szTemp[]=”hello”; return szTemp; } 在上面的例子中,由于szTemp属于栈内分配内存,在函数执行完成后,将自动释放szTemp分配的栈内存,所以调用GetTemp 函数取得的指针指向内存地址是一个无效地址,该内存中存储内容是不可预见的。 使用“野指针”的例子比较好理解。如下: void test() { char * pTemp; pTemp = (char *) malloc(256); if(pTemp != NULL) { str

43、cpy(pTemp,”mytemp1”); printf(“%s\n”,pTemp); } free(pTemp); if(pTemp != NULL) { strcpy(pTemp,”mytemp2”); printf(“%s\n”,pTemp); } } 上面的例子在 strcpy(pTemp,”mytemp2”); 就会出错,因为此时内存已经释放了。我们上面的例子非常容易察觉这个问题。可是我们在实际工程项目中,由于代码量大,程序非常复杂,如果不养成一中良好的习惯,极有可能会出现上面的问题,而且还不太好定位。有人也许会说,我检查下 free

44、函数不就可以了么。关键的问题是,那是您知道是没有将这个指针在释放时设置为 NULL。那么我们在实际项目中,如何杜绝“野指针”呢? 1、指针变量必须进行初始化。 char *p = NULL; char *str = (char *) malloc(100); 2、指针被释放以后,必须将所有指向该块内存区域的指针全部设置为NULL。 3、指针指向的内容是栈分配的内存时,一定要注意作用域的问题。 void Test(void) {   char * p; p=NULL;   {   char szTemp[] =”hello”; p=szT

45、emp; // 注意 szTemp 的生命期  }  printf(“%s\n”, p); // p是“野指针” } 另外,由于指针和数组在很多情况下可以互换,导致混淆,容易犯一些小小的错误。其实,数组要么在静态存储区被创建,要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有内容可以改变。指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。下面列举几种容易出错的例子。 例1: char arr[] = "demo"; arr[0] = 'W'; cout << arr

46、 << endl; char *pPointer = "microsoft"; // 注意pPointer指向常量字符串 pPointer[0] = 'w'; // 编译器不能发现该错误 cout << pPointer << endl; 在上面的例子我们可以发现,数组内容是可以修改的,arr[0] = 'W'; 该句能够正常执行。而pPointer 指向的为一个常量字符串,内容是不可以修改的。pPointer[0] = 'w'; 这句在实际运行中则会导致运行错误,而编译时不能发现这个错误。 例2: char arr[] = "demodemo"; char *point

47、er = arr; cout<< sizeof(arr) << endl; // 8字节 cout<< sizeof(pointer) << endl; // 4字节 从上面的例子中可以看出sizeof可以计算出数组的容量(字节数),而不能计算出指针的容量,除非您在分配内存的时候记住它。 1-4 在Windows 下如何跟踪内存泄露 通过上面章节的学习,我们对内存泄露深恶痛绝,那么如何检查一个程序的内存泄露呢?先介绍一个最简单的方法就是使用VC 调式工具。首先我们来故意产生内存泄露。 1、我们创建一个memleak的支持MFC的工程,工程类型为 win32 Console

48、Application,如图所示,并单击“OK”按钮。 2、在接下来的项目中我们选择“An application that supports MFC.”选择支持MFC的控制台程序。并单击“Finish”。 3、在接下来的界面中单击“ok”按钮完成工程创建。 我们修改memleak.cpp,程序如下: #include "stdafx.h" #include "memleak.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FIL

49、E__; #endif ///////////////////////////////////////////////////////////////////////////// // The one and only application object CWinApp theApp; using namespace std; //故意产生内存泄露的函数 void memleaktest() { char * szTemp= new char[1024]; szTemp=(char *)malloc(1024); } int _tmain(

50、int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") <<

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服