本站小編為你精心準備了C語言動態(tài)存儲參考范文,愿這些范文能點燃您思維的火花,激發(fā)您的寫作靈感。歡迎深入閱讀并收藏。
一、動態(tài)存儲管理的實現(xiàn)
C語言的動態(tài)存儲管理由一組標準庫函數(shù)實現(xiàn),其原型在標準文件<stdlib.h>里描述,需要用這些功能時應包含這個文件。與動態(tài)存儲分配有關的函數(shù)共有四個:
1)存儲分配函數(shù)malloc。其函數(shù)原型是:voidmalloc(unsignedintsize);其作用是在內存的動態(tài)存儲區(qū)中分配一個長度為size的連續(xù)空間。這里的size是一個無符號整型,malloc的返回值為void類型,它分配一片能存放大小為size的數(shù)據(jù)的存儲塊,返回指向該存儲塊起始地址的指針值;如果不能滿足申請(例如內存不足)就返回空指針NULL。所以在調用該函數(shù)時應該檢測返回值是否為NULL并執(zhí)行相應的操作。
2)帶計數(shù)和清0的動態(tài)存儲分配函數(shù)calloc。其函數(shù)原型是:
void*calloc(unsignedn,unsignedsize);參數(shù)size意指數(shù)據(jù)元素的大小,n指要存放的元素個數(shù)。calloc將分配一塊存儲,其大小足以存放n個大小各為size的元素,分配之后還把存儲塊里全部清0(初始化為0值)。如果分配不成功就返回NULL。
3)動態(tài)存儲釋放函數(shù)free。其原型是:voidfree(void*p);其作用是釋放指針p所指的內存區(qū),使這部分內存區(qū)能被其它變量使用。p是調用calloc或malloc函數(shù)時返回的值。free函數(shù)無返回值。如果當時p的值是空指針,free就什么也不做。注意,調用free(p)不會改變p的值(在函數(shù)里不可能改變值參數(shù)p),但被p指向的內存區(qū)的內容卻可能變了(可能由于存儲管理的需要)。釋放后不允許再通過p去訪問已釋放的區(qū),否則也可能引起災難性后果。由于內存區(qū)域有限,每個程序都應盡量節(jié)省資源。當所分配的內存區(qū)域不再使用時,就應及時將它釋放,以便其它的變量或者程序使用,這應該成為習慣。這時就要用到free函數(shù)。
4)分配調整函數(shù)realloc。其函數(shù)原型是:void*realloc(void*p,unsignedn);其作用是更改以前的存儲分配。在調用realloc時,指針變量p的值必須是調用calloc或malloc函數(shù)時返回的值,參數(shù)n表示現(xiàn)在需要的存儲塊大小。realloc在無法滿足新要求時返回NULL,同時也保持p所指的存儲塊的內容不變。如果能夠滿足要求,realloc就返回一片存放大小為n的數(shù)據(jù)的存儲塊,并保證該塊的內容與原塊一致:如果新塊較小,其中將存放著原塊里大小為n的范圍內的那些數(shù)據(jù);如果新塊更大,原有數(shù)據(jù)存在新塊的前面一部分里,新增的部分不自動初始化。如果分配成功,原存儲塊的內容就可能改變了,因此不允許再通過p來使用它。請注意:通過動態(tài)分配得到的塊是一個整體,只能作為一個整體管理。在調用free(p)或者realloc(p,……)時,p當時的值必須是以前通過調用存儲分配函數(shù)得到的,絕不能對指在動態(tài)分配塊里其它位置的指針調用這兩個函數(shù),更不能對并不指向動態(tài)分配塊的指針使用它們,那樣做的后果不堪設想。
二、使用動態(tài)存儲管理的要點
1)必須檢查分配的成功與否。常的解決辦法是,在使用內存之前檢查指針是否為NULL。如果指針p是函數(shù)的參數(shù),那么在函數(shù)的入口處用assert(p!=NULL)進行檢查。如果是用malloc或new來申請內存,則用以下語句來防錯:if((p=(...*)malloc(…))==NULL){……/*對分配未成功情況的處理*/}。2)系統(tǒng)對動態(tài)分配塊的使用不做任何檢查。編程序的人需要保證使用的正確性,絕不可以超出實際存儲塊的范圍進行訪問。例如在使用數(shù)組時經(jīng)常發(fā)生下標“多1”或者“少1”的操作。這種越界訪問可能造成大災難。3)一個動態(tài)分配塊的存在期并不依賴于分配這個塊的地方。在一個函數(shù)里分配的存儲塊的存在期與該函數(shù)的執(zhí)行期無關。函數(shù)結束時不會自動回收這一存儲塊,要回收這種塊,唯一的方法就是通過free釋放(完全由寫程序的人控制)。4)如果在函數(shù)里分配了一個存儲塊,并用局部變量指向它,在這個函數(shù)退出前就必須考慮如何處理這個塊。如果這個塊已經(jīng)沒用了,那么就應該把它釋放掉;如果這個塊還有用(其中保存著有用的數(shù)據(jù)),那么就應該把它的地址賦給存在期更長的變量(例如全局變量),或者把這個地址作為函數(shù)返回值,讓調用函數(shù)的地方去管理它。5)其它情況也可能造成存儲塊丟失。例如給一個指向動態(tài)存儲塊的指針賦其它值,如果此前沒有其它指針指向這個塊,此后就再也無法找到它了。如果一個存儲塊丟失了,在這個程序隨后的運行中,將永遠不能再用這個存儲塊所占的存儲。6)計算器系統(tǒng)里的存儲管理分很多層次。一個程序運行時,操作系統(tǒng)分給它一部分存儲,供它保存代碼和數(shù)據(jù)。其數(shù)據(jù)區(qū)里包括一塊動態(tài)存儲區(qū),由這個程序的動態(tài)存儲管理系統(tǒng)管理。該程序運行中的所有動態(tài)存儲申請都在這塊空間里分配,釋放就是把不用的存儲塊交還程序的動態(tài)存儲管理系統(tǒng)。一旦這個程序結束,操作系統(tǒng)就會收回它占用的所有存儲空間。
三、關于動態(tài)調整策略
我們可以將一個動態(tài)分配的,能存儲許多元素的存儲塊可以看成一個“數(shù)組”,要實現(xiàn)這樣一個能在使用中根據(jù)需要增長的“動態(tài)”數(shù)組,需要考慮所采用的增長策略。
一個簡單而直接的想法是設定一個增量,例如10,一旦存儲區(qū)滿時就把存儲區(qū)擴大10個單元。仔細考慮和計算會發(fā)現(xiàn)這樣做有很大的缺限。實際中對存儲量的需要常常是逐步增加的。一般說,在遇到存儲區(qū)滿時,實際上需要另外分配一塊更大的存儲區(qū),并需要把原塊里已有的元素復制到新塊里。realloc完成這種操作的代價通常與原有的元素個數(shù)成正比。
四、函數(shù)、指針和動態(tài)存儲
如果需要在函數(shù)里處理一組數(shù)據(jù),并把處理結果反應到調用函數(shù)的地方,最合適的辦法就是在函數(shù)調用時提供數(shù)組的起始位置和元素數(shù)目(或者結束位置)。這時函數(shù)完全不必知道用的是程序里定義的數(shù)組變量,還是動態(tài)分配的存儲塊。例如,我們完全可以用如下方式調用篩法函數(shù):intns[1000];intmain(){inti,j;sieve(1000,ns);for(j=1,i=2;i<=n;++i);if(ns[i]==1){printf("%7d%c",i,(j%8?'''''''':''''\n''''));++j;}putchar(''''\n'''');return0;}
在前一節(jié)的篩法程序實例里,我們在主函數(shù)里通過動態(tài)分配取得存儲,而后調用函數(shù)sieve,最后還是由main函數(shù)釋放這塊存儲。這樣,分配和釋放的責任位于同一層次,由同一個函數(shù)(函數(shù)main)完成。這樣做最清晰,易于把握,是最好的處理方案。
但也存在一些情況,其中不能采用上述做法,例如上面的直方圖程序。程序里定義了一個讀入函數(shù),它需要根據(jù)輸入情況確定如何申請動態(tài)存儲。這時的動態(tài)存儲的申請在被調用函數(shù)readscore的內部,該函數(shù)完成向存儲塊里填充數(shù)據(jù)的工作,最后把做好的存儲塊(就像是一個數(shù)組)的地址通過返回值送出來。調用函數(shù)(main)用類型合適的指針接收這個地址值,而后通過這個指針使用這一存儲塊里的數(shù)據(jù)。
首先,這一做法完全正確,因為動態(tài)分配的存儲塊將一直存在到明確調用free釋放它為止。雖然上述存儲塊是在函數(shù)readscores里面分配的,但它的生命周期(生存期)并不隨該函數(shù)的退出而結束。語句:
scores=readscores(&n);使scores得到函數(shù)readscores的運行中申請來并填充好數(shù)據(jù)的存儲塊,在main里繼續(xù)用這個塊是完全沒問題的。當然,采用這種方式,readscores就不應該在退出前釋放該塊。注意:上面的調用除了傳遞有關的數(shù)據(jù)外,實際上還有存儲管理責任的轉移問題。在readscores把一塊存儲的指針通過返回值送出來時,也把釋放這塊存儲的責任轉交給main。這樣,我們也可以看出前面的程序忽略了一件事情,在那里沒有釋放這一存儲塊。應做的修改就是在main的最后加一個釋放語句(當然,由于main的結束就就是整個程序的結束,未釋放的這塊存儲也不會再有用了。如前所述,在這個程序結束后,操作系統(tǒng)將會回收這個程序占用的全部存儲)。
現(xiàn)在考慮readscores的設計里的一個問題。在前面的程序里,readscores通過int指針參數(shù)(實參應該是一個int變量的地址)傳遞實際讀入數(shù)據(jù)的個數(shù)。另一種可能做法是讓函數(shù)返回這一整數(shù)值,例如將其原型改成:intreadscores(???);這樣,我們在main里就可以寫如下形式的調用:if(readscores(……)<=0){……}/*產(chǎn)生錯誤信息并結束程序*/(這一寫法使人想起標準庫的輸入函數(shù)scanf)。如果這樣設計函數(shù),調用readscores的地方就需要通過實參取得函數(shù)里動態(tài)分配的存儲塊地址。也就是說,要從參數(shù)獲得一個指針值。問題是,這個函數(shù)的參數(shù)應該如何定義呢?答案與其它情況完全一樣。如果我們想通過實參取得函數(shù)里送出來的一個int值,就要把一個int變量的地址送進函數(shù),要求函數(shù)間接地給這個變量賦值。同理,現(xiàn)在需要得到一個指針值,就應該通過實參把這種指針變量的地址送進去,讓函數(shù)通過該地址給調用時指定的指針變量賦值。這樣,修改后的函數(shù)readscores的原型應該是:intreadscores(double**dpp);
總的說來,我們介紹了指針、函數(shù)與動態(tài)分配之間的一些關系,并討論了幾種不同的處理技術。只要有可能,在程序里最好使用第一種設計,因為它最清晰,也最不容易出現(xiàn)忘記釋放的情況,如果不得已而采用了其它方式,那么就一定要記得存儲管理責任的交接問題,并在適當?shù)牡胤结尫艅討B(tài)分配的存儲區(qū)。
摘要:本文探討了C語言的動態(tài)存儲管理的實現(xiàn)、使用要點、動態(tài)調整策略等方面內容,以其對有關人員提供參考/
關鍵詞:C語言;動態(tài);存儲;管理。
所謂動態(tài)內存分配就是指在程序執(zhí)行的過程中動態(tài)地分配或者回收存儲空間的分配內存的方法。動態(tài)內存分配不像數(shù)組等靜態(tài)分配方法那樣需要預先分配存儲空間,而是由系統(tǒng)根據(jù)程序的需要實時分配,且分配的大小就是程序要求的大小。