參考文章
儲存區域
-
全域性區域(靜態區域,程式結束後由
os
釋放)- 全域性已初始化區域(已初始化全域性變數 + 未初始化靜態變數)
- 全域性未初始化區域(未初始化全域性變數 + 未初始化靜態變數)
-
棧區(由編譯器自動分配釋放)
- 函式引數、區域性變數、函式返回值等
-
堆區(由程式設計師分配釋放)
- 通過
malloc、calloc、realloc
分配的記憶體
- 通過
-
文字常量區(程式結束後由系統釋放 )
- 常量字串就是放在這裡的
目前已知且自認為能夠理解的儲存區域就以上四種,其他的待補。
實踐證明
1. 全域性區域
在外部宣告的變數以及靜態變數是存放在全域性區域的。
已初始化全域性變數、已初始化靜態變數在同一個區域,如下範例將證明這一點:
int a = 10;
int a1 = 11;
int a2 = 12;
void main(void){
printf("a = %p
" , &a); // 假設 &a = 0x000f24
printf("a1 = %p
" , &a1); // 則 &a1 = 0x000f28
printf("a2 = %p
" , &a2); // 則 &a2 = 0x000f2c
// 以下是干擾的變數
int b = 10;
int b1 = 11;
int b2 = 12;
printf("b = %p
" , &b); // 可以檢視該部分干擾變數的地址
printf("b1 = %p
" , &b1); // 和之前的全域性 a a1 a2 或 之後的 a3 a4
printf("b2 = %p
" , &b2); // 變數地址完全不在同一頻道上
// 靜態變數
static int a3 = 13;
static int a4 = 14;
printf("a3 = %p
" , &a3); // 0x000f30,地址緊接在全域性變數a2地址後,證明統一區域
printf("a4 = %p
" , &a4) // 0x000f34
}
未初始化全域性變數、未初始化靜態變數在同一個區域,以下範例證明(驗證方式同上):
int a;
int a1;
int a2;
void main(void){
printf("&a = %p
" , &a);
printf("&a1 = %p
" , &a1);
printf("&a2 = %p
" , &a2);
// 干擾變數
int b;
int b1;
printf("
");
static a3;
static a4;
printf("&a3 = %p
" , &a3);
printf("&a4 = %p
" , &a4);
}
2. 棧區
函式引數,函式內部區域性變數在記憶體中的的棧區。
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(); // 棧區
}
3. 堆區
使用 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
" , i , *(p + i));
c += *(p + i); // c 棧區,p 棧區, *(p + i) 堆區
}
printf("總成績 = %d
" , c);
system("pause");
}
應用
上述知識有什麼用呢??請看如下範例:
和 棧區 有關的
int * test(){
int arr[2] = {1 , 2};
return arr;
}
void main(void){
int *p = test();
printf("arr[0] = %d
" , *p); // 1
printf("arr[1] = %d
" , *(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
" , *p); // 1
printf("*(p + 1) = %d
" , *(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
" , *p);
printf("%s
" , *p);
}
字串 "test"
是存放在 文字常量 區的,所以在 test
函式呼叫完畢後,其並沒有被釋放,因而兩次呼叫printf
都能正確輸出。