C++ 在程式執行時,將記憶體大致分為程式碼區,全域性區,棧區和堆區四個區域。不同的區域儲存不同的資料,賦予不同的生命週期,能夠更靈活地進行程式設計。
- 程式碼區:存放函式體的二進位制程式碼,由作業系統管理建立,程式碼區時共享的,對於頻繁被執行的程式,只需要存有一份程式碼即可;
- 全域性區:存放全域性變數和靜態變數以及常量,在程式結束後由作業系統釋放;
- 棧區:由編譯其自動分配釋放,存放函式的引數值以及區域性變數等;
- 堆區:一般由程式設計師通過
new
開闢空間,進行分配和釋放,若程式設計師不釋放,則程式結束時由作業系統回收
下面通過一個例子對全域性區,棧區,堆區的資料宣告週期進行說明:
// 全域性變數屬於全域性區,由作業系統管理釋放
int g_a = 1;
int g_b = 2;
int main(void)
{
cout << "g_a 的地址為:\t"<< int(&g_a) << endl;
cout << "g_b 的地址為:\t" << int(&g_b) << endl;
// 建立普通的區域性變數,屬於棧區
int a = 10;
int b = 20;
cout << "a 的地址為:\t" << int(&a) << endl;
cout << "b 的地址為:\t" << int(&b) << endl;
// 建立靜態變數,屬於全域性區
static int s_a = 40;
static int s_b = 50;
cout << "s_a 的地址為:\t" << int(&s_a) << endl;
cout << "s_b 的地址為:\t" << int(&s_b) << endl;
// 程式設計師自己建立變數,屬於堆區
int* d_a = new int(10);
int* d_b = new int(20);
cout << "d_a 的地址為:\t" << int(d_a) << endl;
cout << "d_b 的地址為:\t" << int(d_b) << endl;
}
輸出結果為:
g_a 的地址為: 5300224 g_b 的地址為: 5300228
a 的地址為: 6421316 b 的地址為: 6421304
s_a 的地址為: 5300232 s_b 的地址為: 5300236
d_a 的地址為: 9547944 d_b 的地址為: 9547992
我們從中可以看到,g_a
,g_b
,s_a
,s_b
都屬於全域性區,同理,a
,b
都屬於棧區,d_a
,d_b
都屬於堆區。由於棧區的資料在程式執行結束後會被編譯器自動銷燬,因此不要返回區域性變數的地址,舉例如下:
int* func()
{
int a = 10; // 棧區資料,在程式執行完之後自動釋放
return &a; //雖然返回了a的地址,然而資料在func結束時已經被銷燬
}
int main(void)
{
int* a = func(); // 此時a表示在函式func在棧區開闢的地址,但是其中的資料已被銷燬
cout << "a 的地址為:\t" << int(a) << "a 存放的資料為:\t" << *a << endl;
cout << "a 的地址為:\t" << int(a) << "a 存放的資料為:\t" << *a << endl;
}
輸出結果為:
a 的地址為: 7601480a 存放的資料為: 10
a 的地址為: 7601480a 存放的資料為: 2084553696
由於編譯器會對棧區的資料做一次保留,因此第一條的 cout
語句能夠正常輸出,然而第二次的輸出才是記憶體地址 a
中的資料。
相反,堆區資料由程式設計師自己進行管理,在程式執行完之後並不會自動釋放。當整個程式執行完畢之後會由作業系統釋放。
int* func()
{
int * a = new int(10); // 棧區資料,在程式執行完之後自動釋放
return a; //雖然返回了a的地址,然而資料在func結束時已經被銷燬
}
int main(void)
{
int* a = func(); // 此時a表示在函式func在棧區開闢的地址,但是其中的資料已被銷燬
cout << "a 的地址為:\t" << int(a) << "a 存放的資料為:\t" << *a << endl;
cout << "a 的地址為:\t" << int(a) << "a 存放的資料為:\t" << *a << endl;
}
輸出結果為:
a 的地址為: 23507016a 存放的資料為: 10
a 的地址為: 23507016a 存放的資料為: 10