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); // 程序員使用new在堆區開辟空間,在程序執行完之后自動釋放
return a; //同樣返回了a的地址,然而只要程序沒有運行結束,除非程序員釋放,否則會一直保留
}
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