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