嵌入式Linux上C语言编程实践-第9章动态内存堆与栈_第1页
嵌入式Linux上C语言编程实践-第9章动态内存堆与栈_第2页
嵌入式Linux上C语言编程实践-第9章动态内存堆与栈_第3页
嵌入式Linux上C语言编程实践-第9章动态内存堆与栈_第4页
嵌入式Linux上C语言编程实践-第9章动态内存堆与栈_第5页
已阅读5页,还剩38页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

嵌入式Linux上C语言编程实践--第9章动态内存堆与栈第一页,共43页。2

第9章动态内存的堆与栈第二页,共43页。在嵌入式C语言程序开发中,一个非常重要的内容就是内存的使用,尤其是动态内存的使用。C语言中动态内存主要使用堆和栈来实现。本章主要内容:C语言中程序的存储区域动态内存的堆与栈的特性C语言语言与堆、栈的对应关系堆和栈使用的对比

第三页,共43页。9.1程序内存区域的使用9.1.1静态内存与动态内存C语言程序中数据所使用的内存分类:静态数据存储区只读数据区(ROData)已初始化的读写数据区(RWData)未初始化的读写数据区(BSS)它们都是在程序的编译-连接阶段确定的,在程序运行的初始化阶段,静态存储区将在内存中开辟,其大小和位置在程序的运行过程中都是固定不变的,仅当程序结束时才会被系统收回。动态数据存储区:栈(stack)堆(heap)它们是在程序运行过程中动态分配的,其大小将在程序运行过程动态地变化。第四页,共43页。典型的动态内存管理形式:堆内存和栈内存的分配方向通常是相向的栈内在从高地址向低地地址分配堆内存从低地址向高地址分配动态内存的存储结构栈使用线性存储的方式堆使用链表来实现C语言程序的存储区如图9-1所示。第五页,共43页。第六页,共43页。9.1.2C语言中的动态内存1.栈内存栈是一种先入后出FILO(后进先出LIFO)的内存区域。栈内存由编译器管理栈内存对应内存中的一块区域,有大小限制。栈内存的使用依赖于处理器的硬件机制--栈指针寄存器。栈指针是一个指向栈内部区域的指针,它的值为一个地址,位于栈内存的上、下界之间。栈指针将栈区域分为两个部分:已使用区域未使用区域第七页,共43页。栈内存的增长(生长)方向:向上增长(生长)向下增长(生长)栈指针的变化在向下增长的栈中,初始时栈指针是指向栈的上界(高地址端),随着入栈数据的增加,栈指针将向低地址端变化,即栈指针将变小。在向上增长的栈中则相反。栈内存的重要特性:后进先出(LIFO)栈内存的基本操作:入栈(PUSH)出栈(POP)第八页,共43页。入栈、出栈过程(以向下增长的满栈为例)入栈:先修改指针,再放内容,入栈后,指针指向刚入栈的数据出栈:先取内容,再修改指针,指向下一个要出栈的数据只能对栈顶数据进行操作。栈内存是一端固定(栈底),一端浮动(栈顶)的。AABB低地址高地址上界下界XX第九页,共43页。空栈和满栈空栈:栈指针所指的位置没有数据。入栈时,先放数据,后修改指针,出栈时,先修改指针,再取数据。满栈:栈指针所指的位置总有数据(刚入栈的数据或将要取出的数据)一个系统是满栈or空栈是由处理器结构决定的,与程序的编写无关。前面演示的是满栈的情况第十页,共43页。栈总结:综合栈的生长方向和指针所指单元是否使用,栈可分为四种情况:满递增栈:入栈时,指针先增加,再放数据,入栈后,栈指针指向刚入栈的数据;出栈时,先取数据,指针再减少,出栈后栈指针指向下一数据。满递减栈:入栈时,指针先减小,再放数据,入栈后,栈指针指向刚入栈的数据;出栈时,先取数据,指针再增加,出栈后栈指针指向下一数据。空递增栈:入栈时,先放数据,指针再增加,入栈后,栈指针指向一个新的空位置;出栈时,指针先减少,再取数据,出栈后栈指针指向的位置的数据已被取出(空位置)。空递减栈:入栈时,先放数据,指针再减小,入栈后,栈指针指向一个新的空位置;出栈时,指针先增加,再取数据,出栈后栈指针指向的位置的数据已被取出(空位置)。第十一页,共43页。2.堆内存在一般的编译系统中,堆内存的分配方向和栈内存是相向的:如,栈内存从高地址向低地址增长时,堆内存便从低地址向高地址分配。在C语言中,堆内存的分配和释放是通过程序调用C语言的库函数(malloc()、calloc()、realloc())来实现的。而栈内存是使用处理器的硬件机制实现的。第十二页,共43页。堆内存的分配过程:分配:每调用一次malloc()函数,都将返回一个当前分配到的内存区域的首地址(指针),根据指针可访问分配到的堆内存空间释放:调用free()函数可释放用malloc()函数分配到的内存,不影响其他未释放的堆内存区域的使用。栈内存和堆内存在分配和使用上的区别:栈内存只有一个入口点--栈指针,它的位置是已使用区和未使用区的界限,栈的访问只能通过栈指针及偏移量进行堆内存可有多个入口点,每次分配得到的指针都是访问的入口点,每个指针指向的区域可被单独释放。第十三页,共43页。第十四页,共43页。堆内存的分配原则:堆内存的分配是从堆内存的低地址端开始进行分配,如果低地址端找不到一个满足要求的连续区域,将到较高地址端去分配,直到分配成功时返回分配的内存区域的首地址,或失败时返回NULL。内存碎片频繁采用malloc()和free()函数进行不同大小的内存分配和释放操作,将会在内存中产生内存碎片(太小而无法分配使用的内存区域)。第十五页,共43页。第十六页,共43页。第十七页,共43页。第十八页,共43页。9.2C程序中栈空间的使用9.2.1参数使用栈空间在C语言程序中,栈空间是由编译器管理的,在程序中体现栈空间使用的情况(用途)有:使用栈空间进行函数参数传递参数入栈的顺序:逆序入栈,最右边的参数先入栈。函数中的自动变量分配在栈空间中使用栈空间返回函数值第十九页,共43页。9.2.2自动变量使用栈空间函数参数和函数中的自动变量本质上是一致的,均被分配在栈空间中,均为局部变量。编译器在栈空间中函数参数的后面顺序为函数中的自动变量分配栈区域函数中的静态变量被分配在静态存储区中。第二十页,共43页。9.2.3程序中较大的栈当函数的参数和局部变量为结构体类型等构造数据类型时,它们仍被分配到栈空间中,但要占据更多的栈空间。当参数为结构体类型时,通常采用结构体类型的指针作为函数参数,以节约存储空间和提高程序运行效率。第二十一页,共43页。9.2.4栈空间的特性栈空间的使用完全由编译器管理,在程序中不需要管理栈。函数返回后,函数中在栈上的内容将被释放,所以,函数调用结束后,函数中的形参和自动变量均不能被别的函数访问。函数不能返回自动变量的指针(地址),但可以将自动变量的指针作为参数传递给其他函数。第二十二页,共43页。9.3C程序中的堆空间使用 9.3.1分配和释放堆内存的库函数堆内存区域的分配和释放是通过调用库函数来完成的,这些库函数对应的头文件:stdlib.h或malloc.h实现堆内存分配和释放的C函数主要有4个:void*malloc(size_tsize);voidfree(void*ptr);void*calloc(size_tnmemb,size_tsize);void*realloc(void*ptr,size_tsize);第二十三页,共43页。malloc函数函数原型:void*malloc(size_tsize);参数:size—请求分配的内存大小(字节数)。作用:在堆中分配一个长度为size的连续空间。返回值:分配成功,返回一个指向分配区域的起始地址(指针)。如内存空间不足,返回空指针NULL。(此处:void为无确定类型)第二十四页,共43页。calloc函数函数原型:void*calloc(size_tnmemb,size_tsize);参数:nmemb—需要分配的块数,size—每块字节数作用:在堆中分配nmemb个长度为size的连续空间(nmemb*size)。返回值:分配成功,指向分配区域起始地址(指针),若分配不成功,返回NULL值。与malloc()的区别:calloc()将把分配到的内存区域全部初始化为0。而malloc()不对分配到的内存区域进行初始化。第二十五页,共43页。realloc函数函数原型:void*realloc(void*ptr,size_tsize);参数:ptr—需要重新分配堆内存的指针,size—重新分配的内存大小(字节数)。作用:将ptr指向的内存区域更改为size个字节。返回值:分配成功,指向重新分配的区域的起始地址(指针),若分配不成功,返回NULL值。注意:Ptr应是由calloc或malloc或realloc分配的内存区域指针重新分配后的内存区域中的前面部分的内容与重新分配前内存区域的前面部分的内容相同。如果ptr=NULL,此函数等价于malloc(size);如果size=0,此函数的作用等价于free(ptr)。第二十六页,共43页。free函数函数原型:voidfree(void*ptr);参数:指向待释放区域的指针,它指向的是最近一次calloc或malloc或realloc分配的存储区域。作用:释放由ptr指向的内存区,使这部分内存区能被分配给其他程序使用。返回值:无返回值。第二十七页,共43页。9.3.2库函数使用malloc()和free()以上四个函数中,使用最多的是malloc()和free()两个函数。malloc()函数分配成功后返回的是void*形式的指针,可以根据需要转换成任何类型的指针。free()函数的参数类型是void*形式的指针,可以接受任何类型的指针,但一定要是动态分配函数返回的指针。在使用malloc()时,经常使用表达式sizeof(XXX)或n*sizeof(XXX)形式的参数,其中XXX为某种类型标识符(包括结构体类型)第二十八页,共43页。在访问分配到的内存空间前,应该判断返回的指针是否为NULL,以判断分配是否成功。在使用结束后,应该使用free()释放。释放后将由系统收回,以便下次分配时使用。使用free(ptr)释放ptr指向的内存空间后,ptr指针仍指向原内存区域,但不能访问它所指向的内存区域。因此,释放后应对ptr赋值为NULL。第二十九页,共43页。calloc()与malloc()有两个区别:calloc()将分配到的内在空间初始化为0calloc()有两个参数如:calloc(sizeof(long),10)与malloc(sizeof(long)*10)等价第三十页,共43页。realloc()realloc()实际有三种功能当指针为NULL时,与malloc()作用几乎相同当size为0时,与free()作用相同,释放指针所指的内存空间当指针和不为NULL和size不为0时,将根据指针指向的堆内存区域的情况和指定大小重新分配内存。realloc()重新分配内存时,有三种可能性:缩小内存:指针位置不变扩大内存:指针位置不变,扩大内存:指针位置需要改变,并释放原内存区域扩大内存时,若指针位置发生了改变,还需将原区域中的内容复制到新分配内存区域的前面部分。第三十一页,共43页。9.3.3堆内存的特性合理使用堆内存,可为程序开发带来很大的方便性和灵活性堆内存使用不当,容易出现以下几个问题:如果开辟的内存没有释放,会造成内存泄漏可能出现野指针被使用或释放非法释放指针第三十二页,共43页。内存泄漏简单地说,就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,致使系统性能降低甚至整个系统崩溃。导致内存泄漏常见的三种情况:由程序申请的一块内存后,没释放便对该指针重新赋值,导致没有任何一个指针指向它,那么这块内存就泄露了。如:intptr=(int*)malloc(20);ptr=(int*)malloc(10);将动态分配的内存指针作为函数参数传递给其他函数,在函数中和返回后均未对其进行释放。在函数在进行动态内存分配并将其地址赋值给函数内的动态指针变量,且未调用free()函数释放。第三十三页,共43页。内存泄漏的几种情况(1).常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。(2).偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。(3).一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在主函数中分配内存,在所有函数中都没有释放该内存,所以内存泄漏只会发生一次。(4).隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。第三十四页,共43页。野指针野指针:指向不可用内存区域的指针,而不是NULL指针对这种指针进行操作的话,将会使程序发生不可预知的错误。人们一般不会错用NULL指针,但是“野指针”是很危险的。野指针的成因:一、指针变量没有被初始化。任何指针变量刚被创建时它的缺省值是随机的。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。二、指针p被free或者delete之后,并没有置为NULL,让人误以为p是个合法的指针。函数返回指向自动变量或形参变量的指针。第三十五页,共43页。非法释放指针用free()函数释放不是由malloc()、calloc()、realloc()函数返回的指针所指向的内存空间。对已经被free()函数释放了的指针再次进行释放,即多次释放同一指针。第三十六页,共43页。通过库函数管理堆内存时应注意的问题:调用malloc()、calloc()、realloc()函数后,应判断返回的指针是否为NULL,以判断分配是否成功。每次通过指针访问堆内存空间时应先判断指针是否为NULL。使用完堆内存后必须用free()函数进行释放,以防止内存泄漏。使用free()函数释放后应将该指针置为NULL。,以避免野指针被使用为防止多次释放同一指针,释放前也应判断指针是否为NULL。尽量做到“谁申请,谁释放”。第三十七页,共43页。9.4堆内存和栈内存使用的比较9.4.1利用返回值传递信息函数中通过return<表达式>向主调函数返回值。函数的返回值是通过栈实现的。函数的返回值可以是int、char等基本数据类型,也可以是结构体类型,还可以是指针类型。当函数返回值是指针时,这个指针不能是被调函数中的自动变量、形式参数的地址,但可以指向被调函数内的static变量、堆内存及主调函数栈上的内存。当函数返回值为被调函数中动态分配的堆内存指针时,需要在外部进行释放,否则会造成内存泄漏。C库函数中,返回指针值的函数最典型的有内存处理函数和字符处理函数第三十八页,共43页。返回静态内存地址时,可以是全局的静态变量的地址函数内部的局部静态变量的地址返回被调函数中的局部变量(含形式参数)的地址,将会造成野指针。当需要返回函数内部栈上的内容时,不能使用指针,返回非指针即可,此时将进行值拷贝,返回结构体类型时也一样(P225)。所以返回结构体类型数据时系统开销较大。但通过返回结构体类型数据可以返回多个不同类型的值(分别放在结构体类型的不同成员中)。第三十九页,共43页。9.4.2利用参数传递信息实参和形参的信息传递均是传值(包括指针参数和结构体类

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论