C语言变量在内存中的位置
一、存储区域
存储区域 | 存放变量 |
---|---|
栈区 | 存放局部变量 |
堆区 | 存放new或者malloc出来的对象(一般由程序员分配释放) |
全局区(静态区) | 用于存放全局变量或者静态变量 |
常量区 | 常量(例如字符串字面量、常量值)存储在常量区 |
代码区 | 二进制代码 |
栈区(stack)–由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区(heap)–般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式类似于链表。
全局区(静态区)(static)–全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(RW),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(ZI)。程序结束后有系统释放。 和“栈”一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。
常量区–常量字符串就是放在这里的。程序结束后由系统释放 (RO)
程序代码区–存放函数体的二进制代码。 (RO)
常量存储区—和“全局/静态存储区”一样,通常是用于那些在编译期间就能确定存储大小的常量的存储区,并且在程序运行期间,存储区内的常量是全局可见的。这是一块比较特殊的存储去,他们里面存放的是常量,不允许被修改。
二、介绍
内存存在两种属性:静态分配内存和动态分配内存。
静态分配内存:是在程序编译和链接时就确定好的内存。
动态分配内存:是在程序加载、调入、执行的时候分配/回收的内存。
堆栈
堆和栈都是动态分配内存,两者空间大小都是可变的。
Stack: 栈,按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
通常是用于那些在编译期间就能确定存储大小的变量的存储区,用于在函数作用域内创建,在离开作用域后自动销毁的变量的存储区。通常是局部变量,函数参数等的存储区。他的存储空间是连续的,两个紧密挨着定义的局部变量,他们的存储空间也是紧挨着的。栈的大小是有限的
Heap: 堆,自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。
通常是用于那些在编译期间不能确定存储大小的变量的存储区,它的存储空间是不连续的,一般由malloc(或new)函数来分配内存块,并且需要用free(delete)函数释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题。需要注意的是,两个紧挨着定义的指针变量,所指向的malloc出来的两块内存并不一定的是紧挨着的,所以会产生内存碎片。另外需要注意的一点是,堆的大小几乎不受限制,理论上每个程序最大可达4GB。
每个线程都会有自己的栈,但是堆空间是共用的。
Text & Data & Bss
.text: 也称为代码段(Code),用来存放程序执行代码,同时也可能会包含一些常量(如一些字符串常量等)。该段内存为静态分配,只读(某些架构可能允许修改)。 这块内存是共享的,当有多个相同进程(Process)存在时,共用同一个text段。
.data: 也有的地方叫GVAR(global value),用来存放程序中已经初始化的非零全局变量。静态分配。 data又可分为读写(RW)区域和只读(RO)区域。RO段保存常量所以也被称为.constdata
RW段则是普通非常全局变量,静态变量就在其中
.bss: 存放程序中未初始化的和零值全局变量和静态变量。静态分配,在程序开始时通常会被清零。
低地址到高地址
三、Keil中针对stm32系统进行验证
在MDK中编译后可以看到Code、RO-data、RW-data、ZI-data的大小
Code是存储程序代码的;
RO-data是存储const常量和指令
RW-data是存储初始化值不为0的全局变量
ZI-data是存储未初始化的全局变量或初始化值为0的全局变量
#include "delay.h"
#include "usart.h"
#include "string.h"
#include <stdio.h>
#include <stdlib.h>
int k1 = 1;
int k2;
static int k3 = 2;
static int k4;
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(115200); //串口初始化为115200
while(1)
{
static int m1=2, m2;
int i = 1;
char *p;
char str[10] = "hello";
char *var1 = "123456";
char *var2 = "abcdef";
int *p1=malloc(4);
int *p2=malloc(4);
free(p1);
free(p2);
printf("栈区-变量地址");
printf(" i:%p\r\n", &i);
printf(" p:%p\r\n", &p);
printf(" str:%p\r\n", str);
printf("堆区-动态申请地址");
printf(" %p\r\n", p1);
printf(" %p\r\n", p2);
printf("\r\n.bss段\r\n");
printf("全局外部无初值 k2:%p\r\n", &k2);
printf("静态外部无初值 k4:%p\r\n", &k4);
printf("静态内部无初值 m2:%p\r\n", &m2);
printf("\r\n.data段\r\n");
printf("全局外部有初值 k1:%p\r\n", &k1);
printf("静态外部有初值 k3:%p\r\n", &k3);
printf("静态内部有初值 m1:%p\r\n", &m1);
printf("\r\n常量区\r\n");
printf("文字常量地址 :%p\r\n",var1);
printf("文字常量地址 :%p\r\n",var2);
printf("\r\n代码区\r\n");
printf("程序区地址 :%p\r\n",&main);
}
}
这是keil中的一段程序
其输出结果如图所示
首先是栈区 在程序中i定义为int占4个字节,并且是先定义的 按照栈的规则,地址从上向下增长,所以i的地址是20000654 栈区地址是连续的,所以紧接着的p地址是654-4位200000650
然后是堆区,用malloc分配了两个内存 每一个分配了4个字节,但是打印出来的数据并不是连续的,说明堆返回的内存地址不一定是连续的。
然后是.bss段前面说了.bss段是放未初始化或0值的全局变量和静态变量
然后是.data段存放程序中已经初始化的非零全局变量和静态变量
然后是常量区和代码区
总结
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b;// 栈
char s[] = "abc"; //"abc"在常量区,s在栈上。
char *p2; //栈
char *p3 = "123456"; //123456\0";在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}