數據的存儲區域

參考文章

程序的靜態存儲區,動態存儲區和堆以及棧的關係是什麼?程序員

堆和棧的區別(轉過無數次的文章)hexo

關於C語言中返回局部變量和局部指針變量函數

存儲區域

  1. 全局區域(靜態區域,程序結束後由 os 釋放).net

    • 全局已初始化區域(已初始化全局變量 + 未初始化靜態變量)
    • 全局未初始化區域(未初始化全局變量 + 未初始化靜態變量)
  2. 棧區(由編譯器自動分配釋放)指針

    • 函數參數、局部變量、函數返回值等
  3. 堆區(由程序員分配釋放)code

    • 經過 malloc、calloc、realloc 分配的內存
  4. 文字常量區(程序結束後由系統釋放 )blog

    • 常量字符串就是放在這裏的

目前已知且自認爲可以理解的存儲區域就以上四種,其餘的待補。內存

實踐證實

1. 全局區域

在外部聲明的變量以及靜態變量是存放在全局區域的。字符串

已初始化全局變量、已初始化靜態變量在同一個區域,以下範例將證實這一點: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);
}

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\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 都能正確輸出。

相關文章
相關標籤/搜索