記憶體管理 結構體

2021-08-15 04:45:18 字數 3586 閱讀 5018

一.記憶體存放位置

全域性變數、靜態區域性變數儲存在全域性資料區,初始化的和未初始化的分別儲存在一起;

普通區域性變數儲存在堆疊中;

全域性變數和區域性變數在記憶體裡的區別?

預備知識—程式的記憶體分配:

乙個由c/c++編譯的程式占用的記憶體分為以下幾個部分

1、棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。

2、堆區(heap) — 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os**。注意它與資料結構中的堆是兩回事,分配方式倒是類似於鍊錶,呵呵。

3、全域性區(靜態區)(static)—,全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。 - 程式結束後有系統釋放

4、文字常量區 —常量字串就是放在這裡的。程式結束後由系統釋放

5、程式**區—存放函式體的二進位制**。

注:按我個人理解為了減少記憶體碎片的產生,編譯器可能會將堆區又分為block和heap區。block由一系列大小相等的記憶體塊組成。分配記憶體時先在block中分配,如果block佔滿則從heap區中分配。同時block的大小和個數可以通過配置檔案進行配置,使之達到乙個合適的數量。

二.指標和陣列對比

在cc+程式中,指標和陣列在不少地方可以相互替換著用,讓人產生一種錯覺,以為陣列要麼在靜態儲存區被建立(如全域性陣列),要麼在棧上被建立。陣列名對應著(而不是指向)一塊記憶體,其位址與容量在生命期內保持不變,只有陣列的內容可以改變。指標可以隨時指向任意型別的記憶體塊,它的特徵是「可變」,所以常用指標來操作動態記憶體。指標遠比陣列靈活,但也更危險下面以字串為例比較指標與陣列的特性。

三.無參巨集定義

#define 識別符號 字串

還要說明以下幾點

(1) 巨集定義是用巨集名來表示符串,在巨集展開時又以該字串取代巨集名,這只是種簡單的代換,字串中可以含任何字元,可以是常數,也可以是表示式,預處理程式對它不作任何檢查。如有錯誤,只能在編譯已被巨集展開後的源程式時發現。

(2) 巨集定義不是說明或語句,在行末不必加分號,如加上分號則連分號也一起置換。

(3) 巨集定義必須寫在函式之外,其作用域為巨集定義命令起到源程式結束。如要終止其作用域可使用「#undef」。

(4)巨集名在源程式中若用引號括起來,則預處理程式不對於其進行巨集代換。

(5)巨集定義允許巢狀,在巨集定義的字串中可以使用已經定義的巨集名。在巨集展開時由預處理程式層層代換。

(6)對「輸出格式」作巨集定義,可以減少書寫麻煩。

四.

形成野指標的原因:

兩種。第一種:指標變數沒有初始化。任何指標變數剛被建立時不會自動成為null指標,它的預設值時隨機的;第二種:指標p被

free

或者delete

之後,沒有置為

null

,讓人 誤以為

p是個合法指標。

6、段錯誤以及除錯方法:

原因:沒有許可權;根本就不存在對應的物理存在;

除錯方法:利用gdb逐步查詢段錯誤;

分析core檔案;

段錯誤時啟動除錯;

利用backtrace和

objdump

進行分析。

五、巨集定義:#define

巨集定義是由源程式中的巨集定義命令完成的,巨集代換是由預處理程式自動完成的。

無參巨集定義形式:

#define 識別符號 字串  

帶參巨集定義

#define 巨集名(形參表) 字串

六.檔案包含的雙引號和尖括號的區別:

使用尖括號表示在包含檔案目錄中查詢(包含目錄是由使用者在設定環境時設定的),而不在源檔案目錄去查詢;使用雙引號則表示首先在當前的源檔案目錄中查詢,若未找到才到包含目錄中查詢。

七.條件編譯的三種形式:

第一種形式:#define 識別符號

程式段1

#else

程式段2

#endif

第二種形式:#ifndef 識別符號

程式段1

#else

程式段2

#endif

第三種形式:#if 常量表示式

程式段1

#else

程式段2

#endif

八.引用結構體變數中的成員:

(1)結構體變數名.成員名,如:「

stu1.name」;

(2)結構體指標變數名->成員名,如:「

ps->name」;

(3)(*結構體指標變數)

.成員名,如:「(

*ps)

name」;

(4)結構體變數陣列名.成員名,如:「

stu[0].name」。

九.#define 和 typedef 的區別

typedef 只是為了增加可讀性而為識別符號另起的新名稱(僅僅是個別名),而#define原本在c語言中是為了定義常量。到了c++語言, const,enum、inline的出現使它也漸漸成為了起別名的工具,有時很容易搞不清楚define與typedef兩者到底該用哪個好,如「#define int int 」這樣的語句,用typedef一樣可以完成,用哪個好呢?我主張用typedef,因為在早期的許多c編譯器中這條語句是非法的,只是現今的編譯器又做了擴充。為了盡可能地相容,一般都遵循# define 定義「可讀」的常量和一些巨集語句,而typedef常用來定義關鍵字、冗長的型別的別名。巨集定義只是簡單的字串代換(原地擴充套件),而typedef則不是原地擴充套件,它的新名字具有一定的封裝性,以致新命名的識別符號具有更易定義變數的功能( int*)pint以及下面這行define pint2 int

十.

常見記憶體錯誤及對策

(1) 記憶體分配未成功,卻使用了它。程式設計新手常犯這種錯誤,因為他們沒有意識到記憶體配會不成功。常用解決辦法是,在使用記憶體之前檢查指標是否為null。如果指標p是函式的引數,那麼在函式的入口處用「assert(p!=null)」進行檢查;如果是用malloc或new來申請記憶體,應該用「if(p==null)」或「if(p!=null)」進行防錯處理。

(2) 記憶體分配雖然成功,但是尚未初始化就引用它。犯這種錯誤主要有兩個原因是沒有初始化的觀念;二是誤以為記憶體的預設初值全為零,導致引用初值錯誤(如陣列)記憶體的預設初值究竟是什麼並沒有統一的標準,儘管有些時候為零值,我們寧可信其無不可信其有,所以無論用何種方式建立陣列,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。

(3) 記憶體分配成功並且已經初始化,但操作越過了記憶體的邊界。例如,在使用陣列時經常發生下標「多1」或者「少1」的操作,特別是在for迴圈語句中,迴圈次數很容易搞錯,致陣列操作越界。

(4) 忘記了釋放記憶體,造成記憶體洩漏。含有這種錯誤的函式每被呼叫一次就丟失一塊記憶體。剛開始時系統的記憶體充足,你看不到錯誤,總有一次程式會突然死掉,系統出現記憶體耗盡的提示。動態記憶體的申請與釋放必須配對,程式中malloc與free的使用次數一定要相同,否則有錯誤(new/delete同理)

記憶體管理,結構體

1.如何防止記憶體分配不成功,卻錯誤的使用記憶體?使用前用if p null 來防錯 2.使用free釋放記憶體後,需要將指標置null嗎 需要,不然會產生野指標 3.出現段錯誤的原因?訪問了錯誤的記憶體段,一般是你沒有許可權,或者根本就不存在對應的物理記憶體,常見的是訪問0位址 4.如何快速定位造...

DPDK記憶體管理一 結構體

dpdk的記憶體管理工作主要分布在幾個大的部分 大頁初始化與管理,記憶體管理。使用大頁可以減少頁表開銷,是為了儘量減少tbl miss導致的效能損失。基於大頁,dpdk又進一步細化管理這部分記憶體,使得分配,更加方便。大頁記憶體的基本原理在前面已經解釋過了,這裡就不在繼續。the structure...

記憶體管理 預處理與結構體

1.未初始化的全域性變數 bss段 用來存放那些沒有初始化和初始化為零的全域性變數。2.初始化過的全域性變數 data 裡面存放那些初始化為非零的全域性變數。3.常量資料 rodata段 ro代表ready only,rodata就是用來存放常量資料的。在多個程序是共享的,這樣可以提高執行空間利用率...