內(nèi)存布局
????? C++的內(nèi)存分為5大區(qū),按照地址從高位到低位的順序殉摔,分別為棧區(qū)州胳,堆區(qū),BBS區(qū)逸月,數(shù)據(jù)區(qū)栓撞,代碼區(qū)。
棧區(qū)
???? 編譯器自動(dòng)管理分配碗硬,存放程序中的局部變量瓤湘,函數(shù)參數(shù)值,返回變量恩尾。內(nèi)存分配從高地址向低地址增長(zhǎng)弛说。
堆區(qū)
???? 程序中用戶自定義動(dòng)態(tài)分配的內(nèi)存區(qū)域,內(nèi)存分配從低地址向高地址增長(zhǎng)翰意。
BBS區(qū)
???? 存放程序中被顯示初始化為0的全局變量和靜態(tài)變量剃浇;以及未被初始化的全局變量和靜態(tài)變量。
數(shù)據(jù)區(qū)
???? 存放已初始化的全局變量猎物,靜態(tài)變量虎囚,常量數(shù)據(jù)(如字符串常量)。
代碼區(qū)
???? 存放可執(zhí)行程序的機(jī)器碼蔫磨。
下圖是內(nèi)存布局的基本結(jié)構(gòu)
函數(shù)棧
?????? 如上圖所示淘讥,可執(zhí)行程序文件包含BBS,數(shù)據(jù)區(qū)和代碼區(qū)堤如,可執(zhí)行程序載入內(nèi)存后系統(tǒng)會(huì)保留一些空間蒲列,堆區(qū)和棧區(qū)。堆區(qū)主要是動(dòng)態(tài)分配的內(nèi)存(默認(rèn)情況下)搀罢,而棧區(qū)主要是函數(shù)以及局部變量等蝗岖。
當(dāng)調(diào)用函數(shù)時(shí),一塊連續(xù)內(nèi)存壓入棧榔至;函數(shù)返回時(shí)抵赢,堆棧幀彈出。
堆棧幀包含如下數(shù)據(jù):
1).函數(shù)返回地址
2).局部變量/CPU寄存器數(shù)據(jù)備份
以main調(diào)用fa(),fa調(diào)用fb(),來舉例說明函數(shù)調(diào)用壓棧過程唧取,如下圖所示:
函數(shù)調(diào)用結(jié)束后會(huì)依次出棧铅鲤,如下圖所示:
全局變量
????? 當(dāng)全局變量,靜態(tài)變量未被初始化的時(shí)候枫弟,他們記錄在BBS段邢享。
?????? 處于BBS段的變量值默認(rèn)為0,考慮到這一點(diǎn)淡诗,BBS段內(nèi)部無需存儲(chǔ)大量的零值骇塘,只需記錄字節(jié)個(gè)數(shù)即可伊履。BBS段主要是為了節(jié)省可執(zhí)行文件在磁盤上所占空間,它只記錄變量所需的大小款违。對(duì)未初始化的大型數(shù)組的節(jié)省效率比較明顯唐瀑,舉例如下:
在上述程序中,若不存在BBS段奠货,可執(zhí)行文件將開辟10000*sizeof(int)大小的空間,并全部存儲(chǔ)為0座掘,該變量將在磁盤上占用39KB的空間递惋,但是若存在BBS段,則在可執(zhí)行文件中溢陪,將只是記錄現(xiàn)在BBS段總大小40000即可萍虽,無需占據(jù)39KB的空間。
系統(tǒng)載入可執(zhí)行程序后形真,將BBS段的數(shù)據(jù)載入數(shù)據(jù)段杉编,并將內(nèi)存初始化為0,再調(diào)用程序入口main函數(shù)咆霜。
內(nèi)存對(duì)齊
? ? ? 對(duì)于基本類型邓馒,如float,double蛾坯,int光酣,char等,他們的大小和內(nèi)存占用是一致的脉课。但是對(duì)于結(jié)構(gòu)體而言救军,我們?nèi)〉闷鋝izeof的結(jié)果,會(huì)發(fā)現(xiàn)這個(gè)值可能會(huì)大于結(jié)構(gòu)體成員大小的總和倘零,這是由于結(jié)構(gòu)體內(nèi)部成員進(jìn)行了內(nèi)存對(duì)齊唱遭。
???? 進(jìn)行內(nèi)存對(duì)齊有2個(gè)好處:
??? 1).內(nèi)存對(duì)齊使數(shù)據(jù)讀取更高效
????? 在硬件設(shè)計(jì)中,數(shù)據(jù)讀取的處理器只能從地址為k的倍數(shù)內(nèi)存處開始讀取數(shù)據(jù)呈驶,這種讀取方式相當(dāng)于將內(nèi)存分為多個(gè)塊拷泽,如果內(nèi)存可以從任意位置開始存放的話,數(shù)據(jù)很可能會(huì)被分散到多個(gè)塊中袖瞻,處理分散在多個(gè)塊中的數(shù)據(jù)需要移除首位不需要的字節(jié)跌穗,然后進(jìn)行合并,非常耗時(shí)虏辫。為了提高數(shù)據(jù)的讀取效率蚌吸,程序分配的內(nèi)存不是連續(xù)存儲(chǔ)的,而是按首地址為k的倍數(shù)方式存儲(chǔ)砌庄,這樣就可以一次性讀取數(shù)據(jù)羹唠,而不需要額外的操作奕枢。
?? 2).在某些平臺(tái)上,不進(jìn)行內(nèi)存對(duì)齊會(huì)崩潰?
????? 不是所有的硬件平臺(tái)CPU都能訪問任意地址上的數(shù)據(jù)佩微,某些硬件平臺(tái)只能在某些地址處取得特定類型數(shù)據(jù)缝彬,否則會(huì)拋出硬件異常。
內(nèi)存對(duì)齊規(guī)則
?????? 定義有效對(duì)齊值為結(jié)構(gòu)體中最寬成員和編譯器/用戶指定對(duì)齊值中較小的那個(gè)哺眯。
1).結(jié)構(gòu)體起始地址為有效對(duì)齊值的整數(shù)倍
2).結(jié)構(gòu)體總大小為有效對(duì)齊值得整數(shù)倍
3).結(jié)構(gòu)體第一個(gè)成員偏移值為0谷浅,之后成員的偏移值為min(有效對(duì)齊值,自身大心套俊)的整數(shù)倍一疯。
相當(dāng)于每個(gè)成員要進(jìn)行對(duì)齊,并且每個(gè)結(jié)構(gòu)體也要進(jìn)行對(duì)齊夺姑。
內(nèi)存碎片
?????? 程序的內(nèi)存往往不是緊湊連續(xù)排布的墩邀,而是存在著許多碎片。我們跟據(jù)碎片的原因把碎片分為內(nèi)部碎片和外部碎片兩張類型:
1).內(nèi)部碎片:系統(tǒng)分配的內(nèi)存大于實(shí)際所需的內(nèi)存(內(nèi)存對(duì)齊機(jī)制)
2).外部碎片:不斷分配回收不同大小的內(nèi)存盏浙,由于內(nèi)存分布散亂眉睹,較大內(nèi)存無法分配。
繼承類布局
????? 繼承
????? 如果一個(gè)類繼承自另一個(gè)類废膘,那么它自身數(shù)據(jù)位于父類之后竹海。
????? 含虛函數(shù)的類
????? 如果當(dāng)前類包含虛函數(shù),則會(huì)在類的最前端占用4個(gè)字節(jié)丐黄,用于存儲(chǔ)虛表指針站削,它指向一個(gè)虛函數(shù)表, 虛函數(shù)表中包含當(dāng)前類的所有虛函數(shù)指針孵稽。
?? ?? 虛函數(shù)表?? 下一章詳細(xì)講解许起。