堆和棧的區別(轉過無數次的文章)hexo
全局區域(靜態區域,程序結束後由 os
釋放).net
棧區(由編譯器自動分配釋放)指針
堆區(由程序員分配釋放)code
malloc、calloc、realloc
分配的內存文字常量區(程序結束後由系統釋放 )blog
目前已知且自認爲可以理解的存儲區域就以上四種,其餘的待補。內存
在外部聲明的變量以及靜態變量是存放在全局區域的。字符串
已初始化全局變量、已初始化靜態變量在同一個區域,以下範例將證實這一點:get
int a = 10; int a1 = 11; int a2 = 12; void main(void){ printf("a = %p\n" , &a); // 假設 &a = 0x000f24 printf("a1 = %p\n" , &a1); // 則 &a1 = 0x000f28 printf("a2 = %p\n" , &a2); // 則 &a2 = 0x000f2c // 如下是干擾的變量 int b = 10; int b1 = 11; int b2 = 12; printf("b = %p\n" , &b); // 能夠查看該部分干擾變量的地址 printf("b1 = %p\n" , &b1); // 和以前的全局 a a1 a2 或 以後的 a3 a4 printf("b2 = %p\n" , &b2); // 變量地址徹底不在同一頻道上 // 靜態變量 static int a3 = 13; static int a4 = 14; printf("a3 = %p\n" , &a3); // 0x000f30,地址緊接在全局變量a2地址後,證實統一區域 printf("a4 = %p\n" , &a4) // 0x000f34 }
未初始化全局變量、未初始化靜態變量在同一個區域,如下範例證實(驗證方式同上):
int a; int a1; int a2; void main(void){ printf("&a = %p\n" , &a); printf("&a1 = %p\n" , &a1); printf("&a2 = %p\n" , &a2); // 干擾變量 int b; int b1; printf("\n"); static a3; static a4; printf("&a3 = %p\n" , &a3); printf("&a4 = %p\n" , &a4); }
函數參數,函數內部局部變量在內存中的的棧區。
int test(int a , int b){ // a,棧區 // b,棧區 // a + b 的結果,棧區 return a + b; } void main(void) { int a = 10; // 棧區 int b = 10; // 棧區 const int a = 10; // 棧區 int *p = &a; // 棧區 int *const p1 = &a; // 棧區 int c = test(); // 棧區 }
使用 malloc、calloc、realloc
分配的內存空間屬於堆區。
void main(void) { int len = 2; // 棧區 int *p = (int *)malloc(len * sizeof(int)); // p 在棧區,malloc 分配的內存在堆區! int i = 0; // 棧區 for (; i < len; ++i) { printf("請輸入成績:"); scanf_s("%d" , p + i); // p + i 指向的內存地址在堆區,數據保存在堆區 } // 輸出成績總和 int c = 0; // 棧區 for (i = 0; i < len; ++i) { // 輸出用戶輸入的成績 printf("成績%d = %d\n" , i , *(p + i)); c += *(p + i); // c 棧區,p 棧區, *(p + i) 堆區 } printf("總成績 = %d\n" , c); system("pause"); }
上述知識有什麼用呢??請看以下範例:
和 棧區 有關的
int * test(){ int arr[2] = {1 , 2}; return arr; } void main(void){ int *p = test(); printf("arr[0] = %d\n" , *p); // 1 printf("arr[1] = %d\n" , *(p + 1)); // -858993460 }
爲何會出現這樣的現象呢??這就和內存的存儲區域有關了!
test
函數內的相關變量存放在棧區,因此他們由編譯器自動分配釋放,即:他們會在函數調用完畢後釋放,注意:釋放不是銷燬,只是放棄對棧區的使用權,即棧區內存區域容許被其餘數據覆蓋!
test
執行完畢後,棧區釋放。第一次調用 printf
的時候,在尚未進入 printf
以前獲取 *p
的值,此時棧區數據還在,沒有被其餘數據覆蓋,而後被拷貝一份副本當作變元給 printf
當第一個參數,正確輸出,輸出完畢後,棧區被破壞(數據被覆蓋了,否則放着老數據佔用內存確定是不行的),因此第二次調用 printf
輸出 *(p + 1)
的時候,結果未知,就是由於 p + 1
指向的棧區數據已經被銷燬了。
和 堆區 有關的,請看下面的範例:
int * test(void){ int *p = (int *)malloc(2 * sizeof(int)); *p = 1; *(p + 1) = 2; return p; } void main(void) { int *p = test(); printf("*p = %d\n" , *p); // 1 printf("*(p + 1) = %d\n" , *(p + 1); // 2 }
經過 malloc
分配的內存區域在堆區。因此在 test
函數調用完畢後,分配的 2 * sizeof(int)
Byte空間沒有被釋放,於是經過 *p、*(p + 1)
可以正確訪問數據。
和 文字常量區 有關的,請看下面的範例:
char * test(void){ char *str = "test"; return str; } void main(void) { char *p = test(); printf("%s\n" , *p); printf("%s\n" , *p); }
字符串 "test"
是存放在 文字常量 區的,因此在 test
函數調用完畢後,其並無被釋放,於是兩次調用printf
都能正確輸出。