使用windows平台MinGW版本的GCC编译器,对以下代码片段进行了编译,探究各个变量在内存中的存储位置:
(不同的机器、不同的操作系统的GCC版本可能有差异,因此结论不具有普遍适用性,具体情况需要编译执行此代码自行分析)
代码片段:
#include <stdio.h>
#include <stdlib.h>
int k1 = 1;
int k2;
static int k3 = 2;
static int k4;
int main()
{
static int m1 = 2, m2;
int i = 1;
char *p, *p2;
char str[10] = "hello";
char *q = "hello";
p = (char *)malloc(100);
p2 = (char *)malloc(100);
free(p);
free(p2);
printf("栈区-变量地址 i:%p\n", &i);
printf("栈区-变量地址 p:%p\n", &p);
printf("栈区-变量地址 str:%p\n", str);
printf("栈区-变量地址 q:%p\n", &q);
printf("堆区-动态申请 p: %p\n", p);
printf("堆区-动态申请 p2: %p\n", p2);
printf("全局外部有初值 k1: %p\n", &k1);
printf("全局外部无初值 k2: %p\n", &k2);
printf("静态外部有初值 k3: %p\n", &k3);
printf("静态外部无初值 k4: %p\n", &k4);
printf("内静态有初值 m1: %p\n", &m1);
printf("内静态无初值 m2: %p\n", &m2);
printf("文字常量地址 : %p, %s\n", q, q);
printf("程序区地址 : %p\n", &main);
return 0;
}
运行结果:
分析:
(在保存程序之前编译器应该还要进行预处理进行宏替换等,因为没有详细读过《编译原理》,暂不讨论)
1. 编译器先保存程序内容,所以程序区在内存的地址最小;
2. 编译器优先保存已经赋值初始化的全局变量和静态变量:按照全局外部变量->静态外部变量->静态内部变量的顺序依次存储;
3. 编译器保存过已经赋值初始化的全局变量和静态变量后,开始处理并保存文字常量;
4. 编译器处理并保存文字常量后,开始保存没有赋初始值的全局变量和静态变量:按照静态外部变量->静态内部变量->全局外部变量的顺序依次存储;
5. 将方法的局部变量入栈(因为讨论变量,方法的地址指针暂不讨论)。因为栈的存储规律是后进先出,所以最后定义的变量先入栈,即存在较小的地址,所以也可以推出程序执行时的内存寻址是由低到高。
6. 最后将动态申请的变量按先进先出的顺序存入堆区。
总结如下图所示:
源代码和上图右侧的文字来源:http://www.runoob.com/cprogramming/c-variables.html 的文章评论区,对代码片段增加了一个堆区的内存分配(char *p2),探讨堆内存的内存分配规律。