1. 程式人生 > >淺談C語言和C++中的記憶體管理

淺談C語言和C++中的記憶體管理

面試題

記憶體管理了解多少,說出你知道的

C語言記憶體分配佈局

程序在記憶體中的佈局

最高記憶體地址

      棧區(函式內部的區域性變數,自動釋放 )
  堆疊增長區
      堆區(動態記憶體分配,由程式設計師申請釋放)
    其他段
    .bss段(未初始化的全域性變數)
   .data段(也叫資料段,已經初始化的全域性變數)
   .text段(文字段,程序所執行程式的二進位制檔案)

最低記憶體地址       

C++記憶體分配佈局       

  1. 棧區—函式內區域性變數和函式引數的儲存空間,由編譯器自動釋放
  2. 堆區——動態分配記憶體(malloc)
  3. 自由儲存區——C++根據new定義的抽象的概念,new在這上面分配記憶體
  4. 全域性/靜態變數區——全域性變數和靜態變數存放在這裡,和C不同初始化和未初始化的全域性變數都放在一起
  5. 常量儲存區——存放常量,只讀         

引申堆疊的區別以及malloc/free和new/delete的區別

控制記憶體分配——頻繁地呼叫new/delete(malloc/free)會導致記憶體碎片化,引發堆破碎等問題,所以要管理記憶體的分配

防止堆破碎的通用方法是從不同固定大小的記憶體池中分配不同型別的物件,對每個類過載new/delete就提供了這樣的控制。

過載new/delete,為什麼要過載,兩種方式:1.過載全域性new/delete 2.為每個類過載new[]/delete[]

原因:系統為我們提供的預設的new/delete操作符,缺乏對具體物件的具體分析。系統提供的分配器無論是時間還是空間都存在不足:分配器速度較慢,在分配小型物件時空間浪費嚴重,特別是在一些對效率或記憶體有較大限制的特殊應用中。比如說在嵌入式的系統中,頻繁地進行不定大小的記憶體動態分配很可能會引起嚴重問題,甚至出現堆破碎的風險。再比如在遊戲設計中,效率絕對是一個必須要考慮的問題,而標準new與delete操作符的實現卻存在著天生的效率缺陷。此時,我們可以求助於new與delete操作符的過載,它們給程式帶來更靈活的記憶體分配控制。

過載new/delete的好處:

     a)改善效率

     b)檢測程式碼中的記憶體錯誤

     c)效能優化 

     d)獲得記憶體使用的統計資料

 記憶體管理基本要求  ——不重不漏,不重複delete,不遺漏delete。new/delete要匹配成對出現,這裡的匹配不只是數量,呼叫本身也要匹配,不同類的new和delete不可混淆使用,不能出現“借了東家的東西西家換”的情況。

常見的記憶體錯誤和解決方法

  1. 記憶體分配未成功,就使用了它。在程式設計初期經常會出現這種問題,因為沒有意識到記憶體分配會不成功。常用解決辦法是,在使用記憶體之前檢查指標是否為NULL。如果指標p是函式的引數,那麼在函式的入口處用assert(p!=NULL)進行檢查。如果是用malloc或new來申請記憶體,應該用if(p==NULL) 或if(p!=NULL)進行防錯處理。
  2. 記憶體分配成功但未初始化,就使用它。犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為記憶體的預設初值全為零,導致引用初值錯誤(例如陣列)。 記憶體的預設初值究竟是什麼並沒有統一的標準,儘管有些時候為零值,我們寧可信其無不可信其有。所以無論用何種方式建立陣列,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。
  3. 記憶體分配成功也初始化了,但是在使用時操作越界。例如使用陣列時發生的“多1”“少1”的操作,特別的在for迴圈中,要注意迴圈次數,防止越界。
  4. 忘記釋放記憶體,導致記憶體洩漏。這種錯誤在一開始並不會對程式的執行造成什麼影響,因為此時記憶體充足,但是每次這個函式呼叫的時候,都會佔用一塊記憶體不去釋放,終會有記憶體耗盡的時候,這個時候程式就死掉了。所以必須配對,使用次數要相同。
  5. 釋放了記憶體卻繼續使用它。有三種情況:(a)程式中的物件呼叫關係過於複雜,實在難以搞清楚某個物件究竟是否已經釋放了記憶體,此時應該重新設計資料結構,從根本上解決物件管理的混亂局面。(b)函式的return語句寫錯了,注意不要返回指向“棧記憶體”的“指標”或者“引用”,因為該記憶體在函式體結束時被自動銷燬。(c)使用free或delete釋放了記憶體

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

【規則1】用malloc或new申請記憶體之後,應該立即檢查指標值是否為NULL。防止使用指標值為NULL的記憶體。

【規則2】不要忘記為陣列和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。

【規則3】避免陣列或指標的下標越界,特別要當心發生“多1”或者“少1”操作。 

【規則4】動態記憶體的申請與釋放必須配對,防止記憶體洩漏。【規則5】用free或delete釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”。