第一篇:C語(yǔ)言程序穩(wěn)定性
提高C語(yǔ)言程序運(yùn)行穩(wěn)定性的方法
一、前言
由于C語(yǔ)言的靈活性,用C語(yǔ)言開(kāi)發(fā)出來(lái)的程序容易造成內(nèi)存泄漏、運(yùn)行異常、運(yùn)行結(jié)果不可預(yù)期等程序質(zhì)量問(wèn)題,在用C語(yǔ)言開(kāi)發(fā)程序的過(guò)程中,必須高度重視程序質(zhì)量問(wèn)題,應(yīng)當(dāng)把提高程序穩(wěn)定性的方法加入到項(xiàng)目管理和開(kāi)發(fā)過(guò)程中,最大限度地提高程序的穩(wěn)定性,保證項(xiàng)目的成功開(kāi)發(fā)。在這里總結(jié)多年來(lái)的C語(yǔ)言開(kāi)發(fā)經(jīng)驗(yàn),拿出來(lái)共享以期在這方面能夠得到更多的指教。
二、影響程序穩(wěn)定性的因素
1、內(nèi)存泄漏。造成內(nèi)存泄漏的原因有:
1)、程序有多個(gè)出口,但不能保證在每一個(gè)出口能夠完全釋放掉所有的動(dòng)態(tài)內(nèi)存,如函數(shù)內(nèi)有多個(gè)“return”,但沒(méi)有在每一個(gè)“return”前釋放掉在原已申請(qǐng)但必須釋放的動(dòng)態(tài)內(nèi)存;
2)、對(duì)于“struct”數(shù)據(jù)結(jié)構(gòu),沒(méi)有完全釋放掉每一個(gè)指向動(dòng)態(tài)內(nèi)存的指針,如只釋放指向“struct”數(shù)據(jù)結(jié)構(gòu)指針沒(méi)有釋放“struct”體內(nèi)的指針或某些指針被漏釋放;
3)、對(duì)于用動(dòng)態(tài)內(nèi)存建立的鏈表在釋放時(shí)沒(méi)有一個(gè)一個(gè)結(jié)點(diǎn)去釋放; 4)、一段動(dòng)態(tài)內(nèi)存空間原來(lái)只被一個(gè)指針引用,但在這個(gè)指針引用另外一段內(nèi)存空間的時(shí)候,該段內(nèi)存沒(méi)有被釋放;
5)、對(duì)于在函數(shù)內(nèi)申請(qǐng)但必須在函數(shù)外釋放的動(dòng)態(tài)內(nèi)存,在對(duì)該內(nèi)存使用后忽略該動(dòng)態(tài)內(nèi)存的釋放;
6)、用戶強(qiáng)行退出程序,程序在退出前不能完全釋放掉所有的動(dòng)態(tài)內(nèi)存; 7)、程序運(yùn)行過(guò)程中發(fā)生了異常導(dǎo)致動(dòng)態(tài)內(nèi)存未被釋放。
2、程序運(yùn)行發(fā)生異常。造成異常產(chǎn)生的原因有:
1)、釋放指針時(shí)該指針為空或是一個(gè)已被釋放但釋放后未被置空的指針;
2)、對(duì)于C庫(kù)中的函數(shù),如字符串操作函數(shù),在調(diào)用該類函數(shù)時(shí)實(shí)參為空指針或者改指針沒(méi)有指向可用的內(nèi)存地址空間或者所指向的內(nèi)存空間大小不足以用來(lái)實(shí)現(xiàn)當(dāng)前的字符串操作;
3)、對(duì)于指向一個(gè)“struct”數(shù)據(jù)結(jié)構(gòu)的指針,當(dāng)指針為空時(shí)使用“struct”的分體數(shù)據(jù);
4)、數(shù)組或指針發(fā)生越界操作;
5)、指針指向一個(gè)已被釋放但釋放后未被置空的指針,如一個(gè)全局變量的指針,在一個(gè)地方被釋放后,但指針值未被置空,這時(shí)在另一個(gè)地方引用該指針的值時(shí)會(huì)發(fā)生異常;
6)、更改定義為常量的值;
7)、動(dòng)態(tài)申請(qǐng)完一個(gè)內(nèi)存后,未檢查是否申請(qǐng)成功就調(diào)用了該指針;
8)、對(duì)于一塊連續(xù)的內(nèi)存塊和“struct”數(shù)據(jù)結(jié)構(gòu)在第一次使用時(shí)沒(méi)有做初始化操作。
9)、在用非ASCII(如中文字符、Unicode)編碼時(shí),若使用char*來(lái)申請(qǐng)空間,在用C庫(kù)中的字符串操作函數(shù)來(lái)操作,會(huì)因無(wú)法判斷字符串結(jié)束位置而產(chǎn)生異常。
10)、指針類型強(qiáng)制轉(zhuǎn)換時(shí),當(dāng)強(qiáng)制轉(zhuǎn)換后指針指向的內(nèi)存空間大于原來(lái)指針指向的內(nèi)存空間時(shí)可能會(huì)出現(xiàn)異常(取決于堆或??臻g的結(jié)構(gòu)和大?。?,如把“INT12*”強(qiáng)制轉(zhuǎn)換成“INT32*”,應(yīng)當(dāng)盡量避免指針類型的強(qiáng)制轉(zhuǎn)換;
11)、更改了數(shù)據(jù)結(jié)構(gòu),但代碼沒(méi)有相應(yīng)更新或整個(gè)工程中相關(guān)文件沒(méi)有做相應(yīng)更新;
12)、申請(qǐng)的??臻g或堆空間超出了系統(tǒng)的容量限制;
13)、棧溢出,當(dāng)函數(shù)中定義一個(gè)太大的數(shù)組時(shí)容易造成棧溢出,遞歸調(diào)用太深也容易造成棧舉出;
14)、全局變量使用混亂,造成程序錯(cuò)亂;
16)、內(nèi)存碎片太多,造成內(nèi)存分配失敗而導(dǎo)致程序異常,如建立一個(gè)太長(zhǎng)的鏈表容易造成大量?jī)?nèi)存碎片;
17)、文件操作過(guò)于頻繁(特別是寫(xiě)操作),系統(tǒng)應(yīng)付不過(guò)來(lái)容易造成程序出現(xiàn)異常,這個(gè)在嵌入式系統(tǒng)中較常見(jiàn)。
三、內(nèi)存泄漏預(yù)防措施
1、在代碼審查時(shí),檢查函數(shù)體內(nèi)的每一個(gè)“return”前是否有沒(méi)有釋放必須要釋放的指針;
2、設(shè)計(jì)“struct”數(shù)據(jù)結(jié)構(gòu)時(shí),應(yīng)當(dāng)設(shè)計(jì)相應(yīng)的釋放“struct”指針的函數(shù),并確保所有的“struct”體內(nèi)的指針都被釋放;
3、對(duì)于用動(dòng)態(tài)內(nèi)存建立的鏈表在釋放時(shí)要一個(gè)一個(gè)結(jié)點(diǎn)去釋放, 對(duì)于每一個(gè)鏈表也要有相應(yīng)的鏈表內(nèi)存管理函數(shù),如鏈表的釋放函數(shù);
4、當(dāng)一個(gè)指針變量要指向另一個(gè)動(dòng)態(tài)內(nèi)存地址時(shí)先檢查一下該指針是否有指向另一個(gè)動(dòng)態(tài)內(nèi)存地址,如果有則應(yīng)當(dāng)考慮是否要先釋放掉原先的指向的動(dòng)態(tài)內(nèi)存;
5、在調(diào)用一個(gè)函數(shù)時(shí),對(duì)于函數(shù)的輸出值要確認(rèn)值的內(nèi)存空間是否是在函數(shù)內(nèi)部動(dòng)態(tài)申請(qǐng),如果是則應(yīng)當(dāng)考慮是適當(dāng)?shù)臅r(shí)候把它釋放掉;
6、減少程序的出口的數(shù)目,最好是一個(gè)出口,在出口處理函數(shù)中確保釋放所有的動(dòng)態(tài)內(nèi)存;
7、當(dāng)用戶強(qiáng)行退出時(shí),要考慮在每一個(gè)退出點(diǎn)是否能夠釋放所有的動(dòng)態(tài)內(nèi)存;
8、釋放掉一個(gè)指針?biāo)傅膬?nèi)存空間后,就立即把改指針置為空;
9、少用動(dòng)態(tài)申請(qǐng)內(nèi)存,能用數(shù)組代替的就用數(shù)組的形式;
10、盡量減少全局變量的使用,避免指針指向的混亂;
11、封裝動(dòng)態(tài)內(nèi)存申請(qǐng)和釋放的底層函數(shù),便于檢查內(nèi)存泄漏問(wèn)題;
12、把內(nèi)存泄漏的檢查方法放進(jìn)設(shè)計(jì)代碼中,便于發(fā)現(xiàn)內(nèi)存泄漏。
四、程序運(yùn)行異常預(yù)防措施
1、在釋放指針前先檢查指針是否為空;
2、當(dāng)把指針作為參數(shù)傳入C庫(kù)函數(shù)中的參數(shù)時(shí),先檢查指針是否為空;
3、在函數(shù)體內(nèi),當(dāng)要調(diào)用指針參數(shù)時(shí),先判斷該指針是否為空;
4、當(dāng)要調(diào)用“struct”指針數(shù)據(jù)結(jié)構(gòu)中的分體時(shí)要先判斷該指針是否為空;
5、當(dāng)做指針移動(dòng)操作時(shí)要考慮指針是否會(huì)發(fā)生越界;
6、當(dāng)一個(gè)函數(shù)體內(nèi)可能會(huì)改變參數(shù)中的值時(shí),要避免傳入常量形式的值,在設(shè)計(jì)函數(shù)時(shí)要盡量避免試圖去改變參數(shù)中的值;
7、動(dòng)態(tài)申請(qǐng)完一個(gè)內(nèi)存后要先檢查是否申請(qǐng)成功;
8、對(duì)于一塊連續(xù)的內(nèi)存塊和“struct”數(shù)據(jù)結(jié)構(gòu)在第一次使用時(shí)要做初始化操作,如申請(qǐng)完內(nèi)存后,記得用memset清空內(nèi)存;
9、備案所有的全局變量,考慮全局變量對(duì)程序可能產(chǎn)生的影響,盡量少用全局變量。對(duì)于全局變量的定義最好使用“static”來(lái)申明,不讓其它模塊直接訪問(wèn)該全局變量,并且設(shè)計(jì)好相應(yīng)的操作該全局變量的方法函數(shù),在定義全局變量時(shí)要充分考慮好全局變量的初始化方法和程序結(jié)束時(shí)的處理方法,對(duì)于整個(gè)工程中的全局變量要進(jìn)行登記管理,登記內(nèi)容包括變量名、類型名、定義位置、使用范圍、使用目的、初始化方法、程序結(jié)束時(shí)的處理方法及其它注意事項(xiàng)。
10、在用非ASCII(如中文字符、Unicode)編碼時(shí),要使用unsigned char*來(lái)申請(qǐng)空間,并記住申請(qǐng)空間大小,不要用C庫(kù)中的字符串操作函數(shù)來(lái)操作。
11、記得申請(qǐng)足夠的內(nèi)存,比如,儲(chǔ)存年份應(yīng)該是5個(gè)空間而不是4個(gè),記得保留‘