参考文章
存储区域
-
全局区域(静态区域,程序结束后由
os
释放)- 全局已初始化区域(已初始化全局变量 + 未初始化静态变量)
- 全局未初始化区域(未初始化全局变量 + 未初始化静态变量)
-
栈区(由编译器自动分配释放)
- 函数参数、局部变量、函数返回值等
-
堆区(由程序员分配释放)
- 通过
malloc、calloc、realloc
分配的内存
- 通过
-
文字常量区(程序结束后由系统释放 )
- 常量字符串就是放在这里的
目前已知且自认为能够理解的存储区域就以上四种,其他的待补。
实践证明
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
都能正确输出。