参考文章

程序的静态存储区,动态存储区和堆以及栈的关系是什么?

堆和栈的区别(转过无数次的文章)

关于C语言中返回局部变量和局部指针变量

存储区域

  1. 全局区域(静态区域,程序结束后由 os 释放)

    • 全局已初始化区域(已初始化全局变量 + 未初始化静态变量)
    • 全局未初始化区域(未初始化全局变量 + 未初始化静态变量)
  2. 栈区(由编译器自动分配释放)

    • 函数参数、局部变量、函数返回值等
  3. 堆区(由程序员分配释放)

    • 通过 malloc、calloc、realloc 分配的内存
  4. 文字常量区(程序结束后由系统释放 )

    • 常量字符串就是放在这里的

目前已知且自认为能够理解的存储区域就以上四种,其他的待补。

实践证明

1. 全局区域

在外部声明的变量以及静态变量是存放在全局区域的。

已初始化全局变量、已初始化静态变量在同一个区域,如下范例将证明这一点:

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 都能正确输出。


灰色v碰触
2.9k 声望42 粉丝

爱技术,爱生活