C++堆和棧的區別和聯繫

1、堆和棧的含義

在C++中,內存分爲5個區:堆、佔、自由存儲區、全局/靜態存儲區、常量存儲區
1、: 由系統自動分配和釋放內存,存放函數的參數值,局部變量的值等,分配方式類似於數據結構中的棧 。
2、: 一般由程序員分配和釋放內存(由new申請內存,delete釋放內存), 若程序員不釋放,會造成內存泄露,程序結束時可能由OS回收,分配方式類似於鏈表 。
3、自由存儲區: 是由malloc等分配的內存塊,和堆十分相似,用free來釋放。
4、全局/靜態存儲區: 全局變量和靜態變量被分配到同一塊內存中(在C語言中,全局變量又分爲初始化的和未初始化的,C++中沒有這一區分)。
5、常量存儲區: 裏邊存放常量,不允許修改。

2、堆和棧的區別

堆和棧的區別
堆(heap) 棧(stack)
管理方式 堆中資源由程序員控制 棧資源由編譯器自動管理,無需程序員管理
內存管理機制 系統有一個記錄空閒內存地址的鏈表,當系統收到程序申請時,遍歷該鏈表,尋找第一個空間大於申請空間的堆結點,刪除空閒結點鏈表中的該結點,並將該結點空間分配給程序(大多數系統會在這塊內存空間首地址記錄本次分配的大小,這樣delete才能正確釋放本內存 空間,另外系統會將多餘的部分重新放入空閒鏈表中) 只要棧的剩餘空間大於所申請空間,系統爲程序提供內存,否則報異常提示棧出
空間大小 堆是不連續的內存區域(因爲系統是用鏈表來存儲空閒內存地址,自然不是連續的),堆大小受限於計算機系統中有效的虛擬內存(32bit 系統理論上是4G),所以堆的空間比較靈活,比較大 棧是一塊連續的內存區域,大小是操作系統預定好的,windows下棧大小是2M(也有是1M,在編譯時確定,VS中可設置)
碎片問題 對於堆,頻繁的new/delete會造成大量碎片,使程序效率降低 對於棧,它是一個先進後出的隊列,進出一一對應,不會產生碎片
生長方向 堆向上,向高地址方向增長 棧向下,向低地址方向增長
分配方式 動態分配 棧有靜態分配和動態分配, 靜態分配由編譯器完成(如局部變量分配),動態分配由alloca函數分配,但棧的動態分配的資源由編譯器進行釋放,無需程序員實現
分配效率 堆由C/C++函數庫提供,機制很複雜。所以堆的效率比棧低很多 棧是極其系統提供的數據結構,計算機在底層對棧提供支持,分配專門 寄存 器存放棧地址,棧操作有專門指令

3、測試比較

測試程序如下:

using namespace std;

class Base {
public:
    void f() { cout << "Base::f" << endl; }
    void g() { cout << "Base::g" << endl; }
    void h() { cout << "Base::h" << endl; }
};	

int main()
{
    Base A;
    Base B;
    Base C;
    Base *D = new Base;
    Base E;
    Base *F = new Base;
    Base *G = new Base;;

    cout << "一個class Base的大小爲:" << sizeof(Base) << endl;
    cout << "一個class Base *D的大小爲:" << sizeof(D) << endl;
    cout << "地址A:" << (int*)(&A) << endl;
    cout << "地址B:" << (int*)(&B) << endl;
    cout << "地址C:" << (int*)(&C) << endl;
    cout << "地址D:" << D << endl;
    cout << "地址E:" << (int*)(&E) << endl;
    cout << "地址F:" << F << endl;
    cout << "地址G:" << G << endl;
    
    getchar();
}

測試結果如下:
測試結果
測試結果分析:

1、對於ABCE,其位於棧上,其地址是向低地址方向生長的,內存連續,每次遞減1(Base的大小爲1),類似於一個先進後出的隊列。

2、對於DFG,其位於堆上,其地址是向高地址增長的,而且內存是不連續的。

4、用static來控制變量的儲存方式和可見性

static的內部機制
  靜態數據成員要在程序一開始運行時就必須存在。因爲函數在程序運行中被調用,所以靜態數據成員不能在任何函數內分配空間和初始化。這樣,它的空間分配有三個可能的地方,一是作爲類的外部接口的頭文件,這裏有類聲明;二是類定義的內部實現,這裏有類的成員函數定義;三是應用程序的 main()函數前的全局數據聲明和定義處
  static 被引入以告知編譯器,將變量存儲在程序的靜態存儲區而非棧上空間,靜態數據成員按定義出現的先後順序依次初始化,注意靜態成員嵌套時,要保證所嵌套的成員已經初始化了,消除時的順序是初始化的反順序
static的優勢
  可以節省內存,因爲它是所有對象所公有的,因此,對多個對象來說,靜態數據成員只存儲一處,供所有對象共用。靜態數據成員的值對每個對象都是一樣,但它的 值是可以更新的。只要對靜態數據成員的值更新一次,保證所有對象存取更新後的相同的值,這樣可以提高時間效率。引用靜態數據成員時,採用如下格式:
<類名>::<靜態成員名>

其他:
(1) 類的靜態成員函數是屬於整個類而非類的對象,所以它沒有this指針,這就導致了它僅能訪問類的靜態數據和靜態成員函數。
(2) 不能將靜態成員函數定義爲虛函數。
(3) 由於靜態成員聲明於類中,操作於其外,所以對其取地址操作,就多少有些特殊,變量地址是指向其數據類型的指針,函數地址類型是一個“nonmember 函數指針”。
(4) 由於靜態成員函數沒有 this 指針,所以就差不多等同於 nonmember 函數,結果就產生了一個意想不到的好處:成爲一個 callback 函數,使得我們得以將 c++ 和 c-based x window 系統結合,同時也成功的應用於線程函數身上。
(5) static 並沒有增加程序的時空開銷,相反她還縮短了子類對父類靜態成員的訪問時間,節省了子類的內存空間。
(6) 靜態數據成員在<定義或說明>時前面加關鍵字 static。
(7) 靜態數據成員是靜態存儲的,所以必須對它進行初始化。
(8) 靜態成員初始化與一般數據成員初始化不同:

  • 初始化在類的外部進行,而前面不加 static,以免與一般靜態變量或對象相混淆;
  • 初始化時不加該成員的訪問權限控制符private、public;
  • 初始化時使用作用域運算符來標明它所屬類;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章