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