参考文章
存储区域
-
全局区域(静态区域,程序结束后由
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
都能正确输出。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。