高質量C 程式設計指南 》學習重點四

2021-05-25 06:15:06 字數 3808 閱讀 2106

7 章

記憶體管理

記憶體分配方式有三種:

從靜態儲存區域分配。 內 存在程式編譯的時候就已經分配好,這塊內存在程式的整個執行期間都存在。例 如全域性變數,

static

變數。

在棧上建立。

在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單 元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限。

從堆上分配,亦稱動態 記憶體分配。 程式在執行的時候用

malloc

或new

申請任意多少的記憶體,程式設計師自己負責在何時用

free

或delete

釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。

7.2常見的記憶體錯誤及其對策

發生記憶體錯誤是件非常麻煩的事情。編譯器不能自動發現這些錯誤,通常是在程式執行時才能捕捉到。而這 些錯誤大多沒有明顯的症狀,時隱時現,增加了改錯的難度。有時使用者怒氣沖沖地把你找來,程式卻沒有發生任何問題,你一走,錯誤又發作了。

常見的記憶體錯誤及其對策如下:

記憶體分配未成功,卻使用了它。

程式設計新手常犯這種錯誤,因為他們沒有意識到記憶體分配會不成功。常用解決辦法是,在使用記憶體之前檢查指標是否為

null

。如果指標

p是函式的引數,那麼在函式的入口處用

assert(p!=null)

或if(p!=null) 進 行防錯處理。

記憶體分配雖然成功,但是尚未初始化就引用它。

犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為記憶體的預設初值全為零,導致引用初值錯 誤(例如陣列)。

記憶體的預設初值究竟是 什麼並沒有統一的標準,儘管有些時候為零值,我們寧可信其無不可信其有。所以無論用何種方式建立陣列,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻 煩。

記憶體分配成功並且已經初始化,但操作越過了記憶體的邊界。

例如在使用陣列時經常發生下標「多

1」或者「少

1」的操作。特別是在

for迴圈語句中,迴圈次數很容易搞錯,導致陣列操作越界。

忘記了釋放記憶體,造成記憶體洩露。

動態記憶體的申請與釋放必須配對,程式中

malloc

與free

的使用次數一定要相同,否則肯定有錯 誤(

new/delete

同理)。

釋放了記憶體卻繼續使用它。

有三種情況:

(1)程式中的物件呼叫關係過於複雜,實在難以 搞清楚某個物件究竟是否已經釋放了記憶體,此時應該重新設計資料結構,從根本上解決物件管理的混亂局面。

(2)函式的

return

語句寫錯了,注意不要返回指向「棧記憶體」的「指標」或者「引用」,因為該內存在函式體結束時被自動銷 毀。 (

3)使用

free

或delete

釋放了記憶體後,沒有將指標設定為

null

。導致產生「野指標」。

【規則

7-2-1用malloc 或new 申請記憶體之後,應該立即檢查指標值是否為null 。 防止使用指標值為null 的記憶體 。

【規則

7-2-2不要忘記為陣列和動態記憶體賦初值。

防止 將未被初始化的記憶體作為右值使用 。

【規則

7-2-3避免陣列或指標的下標越界,特別要當心發生「多

1」或者「少

1」操作。

【規則

7-2-4動態記憶體的申請與釋放必須配對,防止記憶體洩漏。

【規則

7-2-5

free

或delete

釋放了記憶體之後,立即將指標設定為

null

,防止產生「野指標」。

c++/c

程式中,指標和陣列在不少地方可以相互替換著用,讓人產生一種錯覺,以為兩者是等價的。

陣列要麼在靜態儲存區被建立(如全域性陣列 ),要麼在棧上被建立。陣列名對應著(而不是指向) 一塊記憶體,其位址與容 量在生命期內保持不變,只有陣列的內容可以改變。

指標可以隨時指向任意型別的記憶體塊,它的特徵是「可變」,所以我們常用指標來操作動態內 存。指標遠比陣列靈活,但也更危險。

下面以字串為

例比較指標與陣列的特性。

7.3.1

修 改內容

示例7-3-1

中, 字元陣列

a的容量是

6個字元,其內容為

hello/0。a

的 內容可以改變,如

a[0]= 『x』

。指標p指向常量字串「

world

」 (位於靜態儲存區,內容為

world/0

),常量字串的內容是不可以被修改的。從語法上看,編譯 器並不覺得語句

p[0]= 『x』

有什麼不妥,但是該語句企圖修改常量字串的內容而導致執行錯 誤。 「

hello」;

『x』;

cout << a << endl;

「world

」 注意p 指向常量字串 『x

』 編譯器不能發現該錯誤

cout << p << endl;

示例7-3-1

修改陣列和指標的內容

7.3.2

內容複製與比較

來判斷,應該用標準庫函式strcmp 進行比較。

比較的不是內容而是位址,應該用庫函式strcmp 來比較。

陣列…char a = "hello";

char b[10];

不 能用b = a;

… 指標…

int len = strlen(a);

char *p = (char *)malloc(sizeof(char)*(len+1));

… 示例7-3-2

陣列和指標的內容複製與比較

7.3.3

計算記憶體容量

用運算子sizeof 可以計算出陣列的容量(位元組數)。 示 例7-3-3 (a )中,sizeof(a) 的值是12 (注 意別忘了

』/0』

)。指標p 指向a ,但是sizeof(p) 的 值卻是4 。

這是因為sizeof(p) 得 到的是乙個指標變數的位元組數,相當於sizeof(char*) ,而不是p 所指的記憶體容量。c++/c 語 言沒有辦法知道指標所指的記憶體容量,除非在申請記憶體時記住它。

注意當陣列作為函式的引數進 行傳遞時,該陣列自動退化為同型別的指標。

示例7-3-3 (b ) 中,不論陣列a 的容量是多少,sizeof(a) 始 終等於sizeof(char *) 。

char a = "hello world";

char *p = a;

cout<< sizeof(a) << endl;   // 12

位元組 cout<< sizeof(p) << endl;   // 4位元組

示例7-3-3(a

) 計算陣列和指標的內 存容量

void func(char a[100])

《 高質量C 程式設計指南 》學習重點十

物件 object 是類 class 的乙個例項 instance 如果將物件比作房子,那麼類就是房子的設計圖紙。所以物件導向設計的重點是類的設計,而不是物件的設計。對於 c 程式而言,設計孤立的類是比較容易的,難的是正確設計基類及其派生類。本章僅僅論述 繼承 inheritance 和 組合 co...

《 高質量C 程式設計指南 》學習重點六

有了 malloc free 為什麼還要 malloc與 free 是c c 語言的標準庫函式,new delete 是c 對於非內部資料型別的物件而言,光用 maloc free 無法滿足動態物件的要求。物件在建立的同時要自動執行建構函式,物件在消亡之前要自動執行析構函式。由於 malloc fr...

《 高質量C 程式設計指南 》學習重點十

物件 object 是類 class 的乙個例項 instance 如果將物件比作房子,那麼類就是房子的設計圖紙。所以物件導向設計的重點是類的設計,而不是對 象的設計。對於c 程式而言,設計孤立的類是比較容易的,難的是正確設計基類及其派生類。本章僅僅論述 繼承 inheritance 和 組合 co...