嵌入式Linux上C語言編程實踐--第9章動態(tài)內(nèi)存堆與棧_第1頁
嵌入式Linux上C語言編程實踐--第9章動態(tài)內(nèi)存堆與棧_第2頁
嵌入式Linux上C語言編程實踐--第9章動態(tài)內(nèi)存堆與棧_第3頁
嵌入式Linux上C語言編程實踐--第9章動態(tài)內(nèi)存堆與棧_第4頁
嵌入式Linux上C語言編程實踐--第9章動態(tài)內(nèi)存堆與棧_第5頁
已閱讀5頁,還剩38頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、第四部分第四部分 C語言高級編程語言高級編程 第9章 動態(tài)內(nèi)存的堆與棧 第10章 函數(shù)指針的使用 第11章 回調(diào)函數(shù)的使用 第第12章章 C語言實現(xiàn)對象編程語言實現(xiàn)對象編程2第第9 9章章 動態(tài)內(nèi)存的堆與棧動態(tài)內(nèi)存的堆與棧在嵌入式在嵌入式C語言程序開發(fā)中,一個非常重要的語言程序開發(fā)中,一個非常重要的內(nèi)容就是內(nèi)存的使用,尤其是動態(tài)內(nèi)存的使用內(nèi)容就是內(nèi)存的使用,尤其是動態(tài)內(nèi)存的使用。C語言中動態(tài)內(nèi)存主要使用堆和棧來實現(xiàn)。語言中動態(tài)內(nèi)存主要使用堆和棧來實現(xiàn)。本章主要內(nèi)容:本章主要內(nèi)容:lC語言中程序的存儲區(qū)域語言中程序的存儲區(qū)域l動態(tài)內(nèi)存的堆與棧的特性動態(tài)內(nèi)存的堆與棧的特性lC語言語言與堆、棧的對應(yīng)

2、關(guān)系語言語言與堆、棧的對應(yīng)關(guān)系l堆和棧使用的對比堆和棧使用的對比9.1 程序內(nèi)存區(qū)域的使用 9.1.1 靜態(tài)內(nèi)存與動態(tài)內(nèi)存靜態(tài)內(nèi)存與動態(tài)內(nèi)存 C語言程序中數(shù)據(jù)所使用的內(nèi)存分類:語言程序中數(shù)據(jù)所使用的內(nèi)存分類:l 靜態(tài)數(shù)據(jù)存儲區(qū)靜態(tài)數(shù)據(jù)存儲區(qū)u只讀數(shù)據(jù)區(qū)(只讀數(shù)據(jù)區(qū)(RO Data)u已初始化的讀寫數(shù)據(jù)區(qū)(已初始化的讀寫數(shù)據(jù)區(qū)(RW Data)u未初始化的讀寫數(shù)據(jù)區(qū)(未初始化的讀寫數(shù)據(jù)區(qū)(BSS) 它們都是在程序的編譯它們都是在程序的編譯-連接階段確定的,在程序運行的初始化階連接階段確定的,在程序運行的初始化階段,靜態(tài)存儲區(qū)將在內(nèi)存中開辟,其段,靜態(tài)存儲區(qū)將在內(nèi)存中開辟,其大小和位置在程序的運行

3、過大小和位置在程序的運行過程中都是固定不變的,僅當(dāng)程序結(jié)束時才會被系統(tǒng)收回程中都是固定不變的,僅當(dāng)程序結(jié)束時才會被系統(tǒng)收回。l 動態(tài)數(shù)據(jù)存儲區(qū):動態(tài)數(shù)據(jù)存儲區(qū):u棧(棧(stack)u堆(堆(heap) 它們是在程序運行過程中動態(tài)分配的,其大小將在程序運行過程它們是在程序運行過程中動態(tài)分配的,其大小將在程序運行過程動態(tài)地變化。動態(tài)地變化。典型的動態(tài)內(nèi)存管理形式典型的動態(tài)內(nèi)存管理形式:堆內(nèi)存和棧內(nèi)存的堆內(nèi)存和棧內(nèi)存的分配方向通常是分配方向通常是相向相向的的l棧內(nèi)在從高地址向低地地址分配棧內(nèi)在從高地址向低地地址分配l堆內(nèi)存從低地址向高地址分配堆內(nèi)存從低地址向高地址分配動態(tài)內(nèi)存的存儲結(jié)構(gòu)動態(tài)內(nèi)存的存

4、儲結(jié)構(gòu)l棧棧 使用使用線性存儲的方式的方式l堆堆 使用使用鏈表來實現(xiàn)來實現(xiàn)C語言程序的存儲區(qū)如圖語言程序的存儲區(qū)如圖9-1所示。所示。9.1.2 C語言中的動態(tài)內(nèi)存1.棧內(nèi)存棧內(nèi)存l棧是一種先入后出棧是一種先入后出FILO(后進先出后進先出LIFO)的內(nèi)存區(qū)域。的內(nèi)存區(qū)域。l棧內(nèi)存由編譯器管理棧內(nèi)存由編譯器管理l棧內(nèi)存對應(yīng)內(nèi)存中的一塊區(qū)域,有大小限制。棧內(nèi)存對應(yīng)內(nèi)存中的一塊區(qū)域,有大小限制。l棧內(nèi)存的使用依賴于處理器的硬件機制棧指針寄存棧內(nèi)存的使用依賴于處理器的硬件機制棧指針寄存器。器。l棧指針是一個指向棧內(nèi)部區(qū)域的指針,它的值為一個地棧指針是一個指向棧內(nèi)部區(qū)域的指針,它的值為一個地址,位于棧

5、內(nèi)存的上、下界之間。址,位于棧內(nèi)存的上、下界之間。l棧指針將棧區(qū)域分為兩個部分棧指針將棧區(qū)域分為兩個部分:u已使用區(qū)域已使用區(qū)域u未使用區(qū)域未使用區(qū)域棧內(nèi)存的增長棧內(nèi)存的增長(生長生長)方向:方向:l向上增長向上增長(生長生長)l向下增長向下增長(生長生長)棧指針的變化棧指針的變化l在向下增長的棧中,初始時棧指針是指向棧的上界在向下增長的棧中,初始時棧指針是指向棧的上界(高地址端),隨著入棧數(shù)據(jù)的增加,棧指針將向(高地址端),隨著入棧數(shù)據(jù)的增加,棧指針將向低地址端變化,即棧指針將變小。低地址端變化,即棧指針將變小。在向上增長的棧在向上增長的棧中則相反。中則相反。棧內(nèi)存的重要特性:棧內(nèi)存的重要特

6、性:后進先出(后進先出(LIFO)棧內(nèi)存的基本操作:棧內(nèi)存的基本操作:l入棧(入棧(PUSH)l出棧(出棧(POP)入棧、出棧過程(以向下增入棧、出棧過程(以向下增長的滿棧為例)長的滿棧為例)l入棧:先修改指針,再放內(nèi)容入棧:先修改指針,再放內(nèi)容,入棧后,指針指向剛?cè)霔5模霔:?,指針指向剛?cè)霔5臄?shù)據(jù)數(shù)據(jù)l出棧:先取內(nèi)容,再修改指針出棧:先取內(nèi)容,再修改指針,指向下一個要出棧的數(shù)據(jù),指向下一個要出棧的數(shù)據(jù)l只能對棧頂數(shù)據(jù)進行操作。只能對棧頂數(shù)據(jù)進行操作。棧內(nèi)存是一端固定(棧底)棧內(nèi)存是一端固定(棧底),一端浮動(棧頂)的。,一端浮動(棧頂)的。AABB低地址低地址高地址高地址上界上界下界下界X

7、X空棧和滿??諚:蜐M棧l空棧:空棧:棧指針?biāo)傅奈恢脹]有數(shù)據(jù)。入棧時,先放棧指針?biāo)傅奈恢脹]有數(shù)據(jù)。入棧時,先放數(shù)據(jù),后修改指針,出棧時,先修改指針,再取數(shù)數(shù)據(jù),后修改指針,出棧時,先修改指針,再取數(shù)據(jù)。據(jù)。l滿棧:滿棧:棧指針?biāo)傅奈恢每傆袛?shù)據(jù)(剛?cè)霔5臄?shù)據(jù)棧指針?biāo)傅奈恢每傆袛?shù)據(jù)(剛?cè)霔5臄?shù)據(jù)或?qū)⒁〕龅臄?shù)據(jù))或?qū)⒁〕龅臄?shù)據(jù))l一個系統(tǒng)是滿棧一個系統(tǒng)是滿棧 or 空棧是由處理器結(jié)構(gòu)決定的,空棧是由處理器結(jié)構(gòu)決定的,與程序的編寫無關(guān)。與程序的編寫無關(guān)。l前面演示的是滿棧的情況前面演示的是滿棧的情況??偨Y(jié):l 綜合棧的生長方向和指針?biāo)竼卧欠袷褂?,棧可分為四種情綜合棧的生長方向和指針?biāo)竼?/p>

8、元是否使用,??煞譃樗姆N情況:況:u滿遞增棧:入棧時,指針先增加,再放數(shù)據(jù),入棧后,棧:入棧時,指針先增加,再放數(shù)據(jù),入棧后,棧指針指向剛?cè)霔5臄?shù)據(jù);出棧時,先取數(shù)據(jù),指針再減少,指針指向剛?cè)霔5臄?shù)據(jù);出棧時,先取數(shù)據(jù),指針再減少,出棧后棧指針指向下一數(shù)據(jù)。出棧后棧指針指向下一數(shù)據(jù)。u滿遞減棧:入棧時,指針先減小,再放數(shù)據(jù),入棧后,棧:入棧時,指針先減小,再放數(shù)據(jù),入棧后,棧指針指向剛?cè)霔5臄?shù)據(jù);出棧時,先取數(shù)據(jù),指針再增加,指針指向剛?cè)霔5臄?shù)據(jù);出棧時,先取數(shù)據(jù),指針再增加,出棧后棧指針指向下一數(shù)據(jù)。出棧后棧指針指向下一數(shù)據(jù)。u空遞增棧:入棧時,先放數(shù)據(jù),指針再增加,入棧后,棧:入棧時,先放

9、數(shù)據(jù),指針再增加,入棧后,棧指針指向一個新的空位置;出棧時,指針先減少,再取數(shù)據(jù)指針指向一個新的空位置;出棧時,指針先減少,再取數(shù)據(jù),出棧后棧指針指向的位置的數(shù)據(jù)已被取出(空位置)。,出棧后棧指針指向的位置的數(shù)據(jù)已被取出(空位置)。u空遞減棧:入棧時,先放數(shù)據(jù),指針再減小,入棧后,棧:入棧時,先放數(shù)據(jù),指針再減小,入棧后,棧指針指向一個新的空位置;出棧時,指針先增加,再取數(shù)據(jù)指針指向一個新的空位置;出棧時,指針先增加,再取數(shù)據(jù),出棧后棧指針指向的位置的數(shù)據(jù)已被取出(空位置)。,出棧后棧指針指向的位置的數(shù)據(jù)已被取出(空位置)。2.堆內(nèi)存堆內(nèi)存l在一般的編譯系統(tǒng)中,在一般的編譯系統(tǒng)中,堆內(nèi)存的分配

10、方向和棧內(nèi)存堆內(nèi)存的分配方向和棧內(nèi)存是相向的是相向的:如,棧內(nèi)存從高地址向低地址增長時,:如,棧內(nèi)存從高地址向低地址增長時,堆內(nèi)存便從低地址向高地址分配。堆內(nèi)存便從低地址向高地址分配。l在在C語言中,語言中,堆內(nèi)存的分配和釋放是通過程序調(diào)用堆內(nèi)存的分配和釋放是通過程序調(diào)用C語言的庫函數(shù)語言的庫函數(shù)(malloc()、calloc()、realloc())來來實現(xiàn)的。而棧內(nèi)存是使用處理器的硬件機制實現(xiàn)的實現(xiàn)的。而棧內(nèi)存是使用處理器的硬件機制實現(xiàn)的。堆內(nèi)存的分配過程:堆內(nèi)存的分配過程:l分配:每調(diào)用一次分配:每調(diào)用一次malloc()函數(shù),都將返回一個當(dāng)前函數(shù),都將返回一個當(dāng)前分配到的內(nèi)存區(qū)域的首

11、地址(指針),根據(jù)指針可訪分配到的內(nèi)存區(qū)域的首地址(指針),根據(jù)指針可訪問分配到的堆內(nèi)存空間問分配到的堆內(nèi)存空間l釋放:調(diào)用釋放:調(diào)用free()函數(shù)可釋放用函數(shù)可釋放用malloc()函數(shù)分配到的函數(shù)分配到的內(nèi)存,不影響其他未釋放的堆內(nèi)存區(qū)域的使用。內(nèi)存,不影響其他未釋放的堆內(nèi)存區(qū)域的使用。棧內(nèi)存和堆內(nèi)存在分配和使用上的區(qū)別:棧內(nèi)存和堆內(nèi)存在分配和使用上的區(qū)別:l棧內(nèi)存只有一個入口點棧指針,它的位置是已使棧內(nèi)存只有一個入口點棧指針,它的位置是已使用區(qū)和未使用區(qū)的界限,棧的訪問只能通過棧指針及用區(qū)和未使用區(qū)的界限,棧的訪問只能通過棧指針及偏移量進行偏移量進行l(wèi)堆內(nèi)存可有多個入口點,每次分配得到

12、的指針都是訪堆內(nèi)存可有多個入口點,每次分配得到的指針都是訪問的入口點,每個指針指向的區(qū)域可被單獨釋放。問的入口點,每個指針指向的區(qū)域可被單獨釋放。堆內(nèi)存的分配原則:堆內(nèi)存的分配原則:l堆內(nèi)存的分配是從堆內(nèi)存的低地址端開始進堆內(nèi)存的分配是從堆內(nèi)存的低地址端開始進行分配,如果低地址端找不到一個滿足要求行分配,如果低地址端找不到一個滿足要求的連續(xù)區(qū)域,將到較高地址端去分配,直到的連續(xù)區(qū)域,將到較高地址端去分配,直到分配成功時返回分配的內(nèi)存區(qū)域的首地址,分配成功時返回分配的內(nèi)存區(qū)域的首地址,或失敗時返回或失敗時返回NULL。內(nèi)存碎片內(nèi)存碎片l頻繁采用頻繁采用malloc()和和free()函數(shù)進行不同

13、大小函數(shù)進行不同大小的內(nèi)存分配和釋放操作,將會在內(nèi)存中產(chǎn)生的內(nèi)存分配和釋放操作,將會在內(nèi)存中產(chǎn)生內(nèi)存碎片內(nèi)存碎片(太小而無法分配使用的內(nèi)存區(qū)域(太小而無法分配使用的內(nèi)存區(qū)域)。)。9.2 C程序中??臻g的使用9.2.1 參數(shù)使用??臻g參數(shù)使用??臻g 在在C語言程序中,??臻g是由編譯器管理的,在程語言程序中,棧空間是由編譯器管理的,在程序中體現(xiàn)序中體現(xiàn)棧空間使用的情況(用途)有??臻g使用的情況(用途)有:l使用??臻g進行函數(shù)參數(shù)傳遞使用棧空間進行函數(shù)參數(shù)傳遞u參數(shù)入棧的順序:參數(shù)入棧的順序:逆序逆序入入棧,棧,最右邊的參數(shù)最右邊的參數(shù)先入棧先入棧。l函數(shù)中的自動變量分配在??臻g中函數(shù)中的自動變量

14、分配在??臻g中l(wèi)使用棧空間返回函數(shù)值使用??臻g返回函數(shù)值9.2.2 自動變量使用棧空間自動變量使用??臻gl函數(shù)參數(shù)和函數(shù)中的自動變量本質(zhì)上是一致的,均函數(shù)參數(shù)和函數(shù)中的自動變量本質(zhì)上是一致的,均被分配在??臻g中,均為局部變量。被分配在??臻g中,均為局部變量。l編譯器在??臻g中函數(shù)參數(shù)的后面順序為函數(shù)中的編譯器在??臻g中函數(shù)參數(shù)的后面順序為函數(shù)中的自動變量分配棧區(qū)域自動變量分配棧區(qū)域l函數(shù)中的靜態(tài)變量被分配在靜態(tài)存儲區(qū)中。函數(shù)中的靜態(tài)變量被分配在靜態(tài)存儲區(qū)中。9.2.3 程序中較大的棧程序中較大的棧l當(dāng)函數(shù)的參數(shù)和局部變量為結(jié)構(gòu)體類型當(dāng)函數(shù)的參數(shù)和局部變量為結(jié)構(gòu)體類型等構(gòu)造數(shù)據(jù)類型時,它們?nèi)员环?/p>

15、配到棧等構(gòu)造數(shù)據(jù)類型時,它們?nèi)员环峙涞綏?臻g中,但要占據(jù)更多的??臻g??臻g中,但要占據(jù)更多的??臻g。l當(dāng)參數(shù)為結(jié)構(gòu)體類型時,通常采用結(jié)構(gòu)當(dāng)參數(shù)為結(jié)構(gòu)體類型時,通常采用結(jié)構(gòu)體類型的指針作為函數(shù)參數(shù),以節(jié)約存體類型的指針作為函數(shù)參數(shù),以節(jié)約存儲空間和提高程序運行效率。儲空間和提高程序運行效率。9.2.4 ??臻g的特性??臻g的特性l??臻g的使用完全由編譯器管理,在程序中??臻g的使用完全由編譯器管理,在程序中不需要管理棧。不需要管理棧。l函數(shù)返回后,函數(shù)中在棧上的內(nèi)容將被釋放函數(shù)返回后,函數(shù)中在棧上的內(nèi)容將被釋放,所以,函數(shù)調(diào)用結(jié)束后,函數(shù)中的形參和,所以,函數(shù)調(diào)用結(jié)束后,函數(shù)中的形參和自動變量均不能

16、被別的函數(shù)訪問自動變量均不能被別的函數(shù)訪問。l函數(shù)不能返回自動變量的指針(地址)函數(shù)不能返回自動變量的指針(地址),但,但可以將自動變量的指針作為參數(shù)傳遞給其他可以將自動變量的指針作為參數(shù)傳遞給其他函數(shù)。函數(shù)。9.3 C程序中的堆空間使用9.3.1 分配和釋放堆內(nèi)存的庫函數(shù)分配和釋放堆內(nèi)存的庫函數(shù)l堆內(nèi)存區(qū)域的分配和釋放是通過調(diào)用庫函數(shù)堆內(nèi)存區(qū)域的分配和釋放是通過調(diào)用庫函數(shù)來完成的,這些庫函數(shù)對應(yīng)的頭文件:來完成的,這些庫函數(shù)對應(yīng)的頭文件:stdlib.h或malloc.hl實現(xiàn)堆內(nèi)存分配和釋放的實現(xiàn)堆內(nèi)存分配和釋放的C函數(shù)主要有函數(shù)主要有4個個:uvoid * malloc(size_t s

17、ize);uvoid free(void * ptr);uvoid * calloc(size_t nmemb,size_t size);uvoid * realloc(void * ptr,size_t size);malloc函數(shù)l函數(shù)原型:void * malloc(size_t size);l參數(shù):size請求分配的內(nèi)存大?。ㄗ止?jié)數(shù))。l作用:在堆中分配一個長度為size的連續(xù)空間。l返回值:分配成功,返回一個指向分配區(qū)域的起始地址(指針)。如內(nèi)存空間不足,返回空指針NULL。 (此處:void為無確定類型) calloc函數(shù)l函數(shù)原型:void * calloc(size_t nme

18、mb,size_t size);l參數(shù):nmemb需要分配的塊數(shù),size每塊字節(jié)數(shù)l作用:在堆中分配nmemb個長度為size的連續(xù)空間(nmemb*size)。l返回值:分配成功,指向分配區(qū)域起始地址(指針),若分配不成功,返回NULL值。l與malloc()的區(qū)別:calloc()將把分配到的內(nèi)存區(qū)域全部初始化為0。而malloc()不對分配到的內(nèi)存區(qū)域進行初始化。realloc函數(shù)l函數(shù)原型:void * realloc(void *ptr,size_t size);l參數(shù):ptr需要重新分配堆內(nèi)存的指針,size重新分配的內(nèi)存大?。ㄗ止?jié)數(shù))。l作用:將ptr指向的內(nèi)存區(qū)域更改為siz

19、e個字節(jié)。l返回值:分配成功,指向重新分配的區(qū)域的起始地址(指針),若分配不成功,返回NULL值。l注意:uPtr應(yīng)是由應(yīng)是由calloc或malloc或realloc分配的內(nèi)存區(qū)域指針u重新分配后的內(nèi)存區(qū)域中的前面部分的內(nèi)容與重新分配前內(nèi)存區(qū)域的前面部分的內(nèi)容相同。u如果ptrNULL,此函數(shù)等價于malloc(size);如果size0,此函數(shù)的作用等價于free(ptr)。free函數(shù)l函數(shù)原型:void free(void *ptr);l參數(shù):指向待釋放區(qū)域的指針,它指向的是最近一次calloc或malloc或realloc分配的存儲區(qū)域。l作用:釋放由ptr指向的內(nèi)存區(qū),使這部分內(nèi)存

20、區(qū)能被分配給其他程序使用。l返回值:無返回值。 9.3.2 庫函數(shù)使用malloc()和free()l以上四個函數(shù)中,使用最多的是以上四個函數(shù)中,使用最多的是malloc()和和free()兩兩個函數(shù)。個函數(shù)。lmalloc()函數(shù)分配成功后返回的是函數(shù)分配成功后返回的是void*形式的指針形式的指針,可以根據(jù)需要轉(zhuǎn)換成任何類型的指針。,可以根據(jù)需要轉(zhuǎn)換成任何類型的指針。lfree()函數(shù)的參數(shù)類型是函數(shù)的參數(shù)類型是void * 形式的指針,可以接形式的指針,可以接受任何類型的指針,但一定受任何類型的指針,但一定要要是動態(tài)分配函數(shù)返回是動態(tài)分配函數(shù)返回的指針。的指針。l在使用在使用malloc

21、()時,經(jīng)常使用表達式時,經(jīng)常使用表達式sizeof(XXX)或或n*sizeof(XXX)形式的參數(shù),其中形式的參數(shù),其中XXX為某種類型標(biāo)為某種類型標(biāo)識符(包括結(jié)構(gòu)體類型)識符(包括結(jié)構(gòu)體類型)l在訪問分配到的內(nèi)存空間前,應(yīng)該判斷返回在訪問分配到的內(nèi)存空間前,應(yīng)該判斷返回的指針是否為的指針是否為NULL,以判斷分配是否成功,以判斷分配是否成功。l在使用結(jié)束后,應(yīng)該使用在使用結(jié)束后,應(yīng)該使用free()釋放。釋放釋放。釋放后將由系統(tǒng)收回,以便下次分配時使用。后將由系統(tǒng)收回,以便下次分配時使用。l使用使用free(ptr)釋放釋放ptr指向的內(nèi)存空間后,指向的內(nèi)存空間后,ptr指針仍指向原內(nèi)存

22、區(qū)域,但不能訪問它指針仍指向原內(nèi)存區(qū)域,但不能訪問它所指向的內(nèi)存區(qū)域。因此,釋放后應(yīng)對所指向的內(nèi)存區(qū)域。因此,釋放后應(yīng)對ptr賦值為賦值為NULL。calloc()l與與malloc()有兩個區(qū)別:有兩個區(qū)別:ucalloc()將分配到的內(nèi)在空間初始化為將分配到的內(nèi)在空間初始化為0ucalloc()有兩個參數(shù)有兩個參數(shù)l如:如:ucalloc(sizeof(long),10)u與與malloc(sizeof(long)*10)等價等價realloc()lrealloc()實際有三種功能實際有三種功能u當(dāng)指針為當(dāng)指針為NULL時,與時,與malloc()作用幾乎相同作用幾乎相同u當(dāng)當(dāng)size為為

23、0時,與時,與free()作用相同,釋放指針?biāo)傅膬?nèi)存空作用相同,釋放指針?biāo)傅膬?nèi)存空間間u當(dāng)指針和不為當(dāng)指針和不為NULL和和size不為不為0時,將根據(jù)指針指向的堆時,將根據(jù)指針指向的堆內(nèi)存區(qū)域的情況和指定大小重新分配內(nèi)存。內(nèi)存區(qū)域的情況和指定大小重新分配內(nèi)存。lrealloc()重新分配內(nèi)存時,有三種可能性:重新分配內(nèi)存時,有三種可能性:u縮小內(nèi)存:指針位置不變縮小內(nèi)存:指針位置不變u擴大內(nèi)存:指針位置不變,擴大內(nèi)存:指針位置不變,u擴大內(nèi)存:指針位置需要改變,并釋放原內(nèi)存區(qū)域擴大內(nèi)存:指針位置需要改變,并釋放原內(nèi)存區(qū)域l擴大內(nèi)存時,若指針位置發(fā)生了改變,還需將原區(qū)擴大內(nèi)存時,若指針位置

24、發(fā)生了改變,還需將原區(qū)域中的內(nèi)容復(fù)制到新分配內(nèi)存區(qū)域的前面部分。域中的內(nèi)容復(fù)制到新分配內(nèi)存區(qū)域的前面部分。9.3.3 堆內(nèi)存的特性合理使用堆內(nèi)存,可為程序開發(fā)帶來很大的方合理使用堆內(nèi)存,可為程序開發(fā)帶來很大的方便性和靈活性便性和靈活性堆內(nèi)存使用不當(dāng),容易出現(xiàn)以下幾個問題:堆內(nèi)存使用不當(dāng),容易出現(xiàn)以下幾個問題:l如果開辟的內(nèi)存沒有釋放,會造成內(nèi)存泄漏如果開辟的內(nèi)存沒有釋放,會造成內(nèi)存泄漏l可能出現(xiàn)野指針被使用或釋放可能出現(xiàn)野指針被使用或釋放l非法釋放指針非法釋放指針內(nèi)存泄漏內(nèi)存泄漏l簡單地說,就是申請了一塊內(nèi)存空間,使用完畢后簡單地說,就是申請了一塊內(nèi)存空間,使用完畢后沒有釋放掉。沒有釋放掉。l

25、它的一般表現(xiàn)方式是程序運行時間越長,占用內(nèi)存它的一般表現(xiàn)方式是程序運行時間越長,占用內(nèi)存越多,最終用盡全部內(nèi)存,致使系統(tǒng)性能降低甚至越多,最終用盡全部內(nèi)存,致使系統(tǒng)性能降低甚至整個整個系統(tǒng)崩潰。系統(tǒng)崩潰。導(dǎo)致內(nèi)存泄漏常見的三種情況:導(dǎo)致內(nèi)存泄漏常見的三種情況:l由程序申請的一塊內(nèi)存后,沒釋放便對該指針重新由程序申請的一塊內(nèi)存后,沒釋放便對該指針重新賦值,導(dǎo)致沒有任何一個指針指向它,那么這塊內(nèi)賦值,導(dǎo)致沒有任何一個指針指向它,那么這塊內(nèi)存就泄露了。如:存就泄露了。如: int ptr=(int *)malloc(20); ptr=(int *)malloc(10);l將動態(tài)分配的內(nèi)存指針作為函數(shù)

26、參數(shù)傳遞給其他函將動態(tài)分配的內(nèi)存指針作為函數(shù)參數(shù)傳遞給其他函數(shù),在函數(shù)中和返回后均未對其進行釋放。數(shù),在函數(shù)中和返回后均未對其進行釋放。l在函數(shù)在進行動態(tài)內(nèi)存分配并將其地址賦值給函數(shù)在函數(shù)在進行動態(tài)內(nèi)存分配并將其地址賦值給函數(shù)內(nèi)的動態(tài)指針變量內(nèi)的動態(tài)指針變量,且未調(diào)用,且未調(diào)用free()函數(shù)釋放函數(shù)釋放。 內(nèi)存泄漏的幾種情況內(nèi)存泄漏的幾種情況l (1). 常發(fā)性內(nèi)存泄漏。 發(fā)生內(nèi)存泄漏的代碼會被多次執(zhí)行到,每次被執(zhí)行的時候都會導(dǎo)發(fā)生內(nèi)存泄漏的代碼會被多次執(zhí)行到,每次被執(zhí)行的時候都會導(dǎo)致一塊內(nèi)存泄漏。致一塊內(nèi)存泄漏。 l (2). 偶發(fā)性內(nèi)存泄漏。 發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過

27、程下才會發(fā)生發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過程下才會發(fā)生。常發(fā)。常發(fā)性和偶發(fā)性是相對的。對于特定的環(huán)境,偶發(fā)性的也許就性和偶發(fā)性是相對的。對于特定的環(huán)境,偶發(fā)性的也許就變成了常發(fā)性的。所以測試環(huán)境和測試方法對檢測內(nèi)存泄漏至關(guān)變成了常發(fā)性的。所以測試環(huán)境和測試方法對檢測內(nèi)存泄漏至關(guān)重要。重要。 l (3). 一次性內(nèi)存泄漏。 發(fā)生內(nèi)存泄漏的代碼只會被執(zhí)行一次,或者由于算法上的缺陷發(fā)生內(nèi)存泄漏的代碼只會被執(zhí)行一次,或者由于算法上的缺陷,導(dǎo)致總會有一塊僅且一塊內(nèi)存發(fā)生泄漏。比如,在主函數(shù)中分,導(dǎo)致總會有一塊僅且一塊內(nèi)存發(fā)生泄漏。比如,在主函數(shù)中分配內(nèi)存,在所有函數(shù)中都沒有釋放該內(nèi)存,所以

28、內(nèi)存泄漏只會發(fā)配內(nèi)存,在所有函數(shù)中都沒有釋放該內(nèi)存,所以內(nèi)存泄漏只會發(fā)生一次。生一次。 (4). 隱式內(nèi)存泄漏。 程序在運行過程中不停的分配內(nèi)存,但是直到結(jié)束的時候才釋程序在運行過程中不停的分配內(nèi)存,但是直到結(jié)束的時候才釋放內(nèi)存。嚴(yán)格的說這里并沒有發(fā)生內(nèi)存泄漏,因為最終程序釋放放內(nèi)存。嚴(yán)格的說這里并沒有發(fā)生內(nèi)存泄漏,因為最終程序釋放了所有申請的內(nèi)存。但是對于一個服務(wù)器程序,需要運行幾天,了所有申請的內(nèi)存。但是對于一個服務(wù)器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內(nèi)存也可能導(dǎo)致最終耗盡系統(tǒng)的所幾周甚至幾個月,不及時釋放內(nèi)存也可能導(dǎo)致最終耗盡系統(tǒng)的所有內(nèi)存。所以,我們稱這類內(nèi)存泄漏為隱式內(nèi)存

29、泄漏。有內(nèi)存。所以,我們稱這類內(nèi)存泄漏為隱式內(nèi)存泄漏。 野指針野指針l野指針:野指針:指向不可用內(nèi)存區(qū)域的指針,而不是指向不可用內(nèi)存區(qū)域的指針,而不是NULL指指針針l對這種指針進行操作的話,將會使程序發(fā)生不可預(yù)知的對這種指針進行操作的話,將會使程序發(fā)生不可預(yù)知的錯誤。錯誤。 l人們一般不會錯用人們一般不會錯用NULL指針,但是指針,但是“野指針野指針”是很危險是很危險的。的。l野指針的成因:野指針的成因: u一、指針變量沒有被初始化。任何指針變量剛被創(chuàng)建時它的缺省一、指針變量沒有被初始化。任何指針變量剛被創(chuàng)建時它的缺省值是隨機的。所以,指針變量在創(chuàng)建的同時應(yīng)當(dāng)被初始化,要么值是隨機的。所以,

30、指針變量在創(chuàng)建的同時應(yīng)當(dāng)被初始化,要么將指針設(shè)置為將指針設(shè)置為NULL,要么讓它指向合法的內(nèi)存。,要么讓它指向合法的內(nèi)存。 u二、指針二、指針p被被free或者或者delete之后,并沒有置為之后,并沒有置為NULL,讓人誤以,讓人誤以為為p是個合法的指針。是個合法的指針。u函數(shù)返回指向自動變量或形參變量的指針。函數(shù)返回指向自動變量或形參變量的指針。非法釋放指針非法釋放指針l用用free()函數(shù)釋放不是由函數(shù)釋放不是由malloc()、calloc()、realloc()函數(shù)返回的指針?biāo)赶虻膬?nèi)存空間。函數(shù)返回的指針?biāo)赶虻膬?nèi)存空間。l對已經(jīng)被對已經(jīng)被free()函數(shù)釋放了的指針再次進行釋放,

31、即函數(shù)釋放了的指針再次進行釋放,即多次釋放同一指針。多次釋放同一指針。通過庫函數(shù)管理堆內(nèi)存時應(yīng)注意的問題:通過庫函數(shù)管理堆內(nèi)存時應(yīng)注意的問題:l調(diào)用調(diào)用malloc()、calloc()、realloc()函數(shù)后,應(yīng)判斷函數(shù)后,應(yīng)判斷返回的指針是否為返回的指針是否為NULL,以判斷分配是否成功,以判斷分配是否成功。l每次通過指針訪問堆內(nèi)存空間時應(yīng)先判斷指針是每次通過指針訪問堆內(nèi)存空間時應(yīng)先判斷指針是否為否為NULL。l使用完堆內(nèi)存后必須用使用完堆內(nèi)存后必須用free()函數(shù)進行釋放,以防函數(shù)進行釋放,以防止內(nèi)存泄漏。止內(nèi)存泄漏。l使用使用free()函數(shù)釋放后應(yīng)將該指針置為函數(shù)釋放后應(yīng)將該指針

32、置為NULL。,。,以避免野指針被使用以避免野指針被使用l為防止多次釋放同一指針,釋放前也應(yīng)判斷指針為防止多次釋放同一指針,釋放前也應(yīng)判斷指針是否為是否為NULL。l盡量做到盡量做到“誰申請,誰釋放誰申請,誰釋放”。9.4 堆內(nèi)存和棧內(nèi)存使用的比較9.4.1 利用返回值傳遞信息利用返回值傳遞信息l函數(shù)中通過函數(shù)中通過return 向主調(diào)函數(shù)返回值。向主調(diào)函數(shù)返回值。l函數(shù)的返回值是通過棧實現(xiàn)的。函數(shù)的返回值是通過棧實現(xiàn)的。l函數(shù)的返回值可以是函數(shù)的返回值可以是int、char等基本數(shù)據(jù)類型,也可等基本數(shù)據(jù)類型,也可以是結(jié)構(gòu)體類型,還可以是指針類型。以是結(jié)構(gòu)體類型,還可以是指針類型。l當(dāng)函數(shù)返回

33、值是指針時,這個指針當(dāng)函數(shù)返回值是指針時,這個指針不能是被調(diào)函數(shù)中不能是被調(diào)函數(shù)中的自動變量、形式參數(shù)的地址的自動變量、形式參數(shù)的地址,但可以指向被調(diào)函數(shù),但可以指向被調(diào)函數(shù)內(nèi)的內(nèi)的static變量、堆內(nèi)存及主調(diào)函數(shù)棧上的內(nèi)存。變量、堆內(nèi)存及主調(diào)函數(shù)棧上的內(nèi)存。l當(dāng)函數(shù)返回值為被調(diào)函數(shù)中動態(tài)分配的堆內(nèi)存指針時當(dāng)函數(shù)返回值為被調(diào)函數(shù)中動態(tài)分配的堆內(nèi)存指針時,需要在外部進行釋放,否則會造成內(nèi)存泄漏。,需要在外部進行釋放,否則會造成內(nèi)存泄漏。lC庫函數(shù)中,返回指針值的函數(shù)最典型的有內(nèi)存處理庫函數(shù)中,返回指針值的函數(shù)最典型的有內(nèi)存處理函數(shù)和字符處理函數(shù)函數(shù)和字符處理函數(shù)返回靜態(tài)內(nèi)存地址時,可以是返回靜

34、態(tài)內(nèi)存地址時,可以是l全局的靜態(tài)變量的地址全局的靜態(tài)變量的地址l函數(shù)內(nèi)部的局部靜態(tài)變量的地址函數(shù)內(nèi)部的局部靜態(tài)變量的地址返回被調(diào)函數(shù)中的局部變量(含形式參數(shù))的地址返回被調(diào)函數(shù)中的局部變量(含形式參數(shù))的地址,將會造成野指針。,將會造成野指針。當(dāng)需要返回函數(shù)內(nèi)部棧上的內(nèi)容時,不能使用指針當(dāng)需要返回函數(shù)內(nèi)部棧上的內(nèi)容時,不能使用指針,返回非指針即可,此時將進行值拷貝,返回結(jié)構(gòu),返回非指針即可,此時將進行值拷貝,返回結(jié)構(gòu)體類型時也一樣(體類型時也一樣(P225) 。所以返回結(jié)構(gòu)體類型數(shù)。所以返回結(jié)構(gòu)體類型數(shù)據(jù)時系統(tǒng)開銷較大。但通過返回結(jié)構(gòu)體類型數(shù)據(jù)可據(jù)時系統(tǒng)開銷較大。但通過返回結(jié)構(gòu)體類型數(shù)據(jù)可以返回多個不同類型的值(分別放在結(jié)構(gòu)體類型的以返回多個不同類型的值(分別放在結(jié)構(gòu)體類型的不同成員中)不同成員中) 。9.4.2 利用參數(shù)傳遞信息實參和形參的信息傳遞均是傳值(包括指針參實參和形參的信息傳遞均是傳值(包括指針參數(shù)和結(jié)構(gòu)體類型參數(shù))數(shù)和結(jié)構(gòu)體類型參數(shù))數(shù)組名作為實參時,形參可定義為數(shù)組,也可數(shù)組名作為實參時,形參可定義為數(shù)組,也可以定義為指針,但編譯器都將其處理成指針。以定義為指針,但編譯器都將其處理成指針。當(dāng)參數(shù)為指針變量時,形參變量接受實參變量當(dāng)參數(shù)為指針變量時,形參變量接受實參變量的值,使得形參變量與實參變量均指向同一位的值,使得形參變量與實參變量均

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論