C语言中变量的内存分配及储存位置的探究

C语言变量的内存分配及储存位置的探究

最近学习C语言,对变量的储存位置很不清楚,后来通过多次查询资料有了自己的见解,特写此博客分享自己对C语言在内存中的储存过程的理解,如有错误望指正。

一、虚拟内存

首先要知道,我们在应用程序中所使用的地址均是虚拟内存地址,在32位操作系统中,每一个进程所占用的虚拟内存是4G,4G的虚拟内存又分为1G的内核空间和3G的用户空间,内核空间是当前主机中所有进程共有的,用户空间是当前进程私有的,下面讲解的内存区域的划分,指的是用户空间的划分。
虚拟内存划分

二、虚拟内存用户空间的区域划分

在了解了虚拟内存的基础上,我们继续看用户空间的区域划分。内存主要分为以下五个区域:
1、栈区(又称堆栈区)
存放函数的参数值、非静态局部变量等,由编译器自动分配和释放,通常在函数执行完后就释放了,其操作方式类似于数据结构中的栈。栈内存分配运算内置于CPU的指令集,效率很高,但是分配的内存量有限。
2、堆区
就是通过malloc动态分配的内存块,编译器不会负责它们的释放工作,需要用程序区释放,如果不在程序中释放则一般整个程序结束后会被系统回收内存资源。分配方式类似于数据结构中的链表。“内存泄漏”通常说的就是堆区。
3、静态区(又称静态全局区)
全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,所以静态区又分为两个段(data、bss后文详细解释)。程序结束后,由系统释放。
4、代码区
存放代码的区域
5、文字常量区
存放只读数据,如常量字符串

所以变量所在内存区(下文还有变量所在段)如下:

全局变量 -----> 静态区
静态变量 -----> 静态区
非静态局部变量、形参、返回值 -----> 栈区
动态申请的变量 -----> 堆区
代码 -----> 代码区
常量字符串 -----> 文字常量区

三、程序编译后的分段情况

源文件在编译之后生成的.o文件已经将程序进行了分段,主要有以下四个段:
1、代码段 .text
text段是程序代码段,它是由编译器在编译连接时自动计算的,当你在链接定位文件中将该符号放置在代码段后,那么该符号表示的值就是代码段大小,编译连接时,该符号所代表的值会自动代入到源程序中。
2、数据段(细分为两个段)
①已初始化段(又称全局初始化段、静态初始化段) .data
data包含静态初始化的数据,所以有初值的全局变量和static变量在data区。段的起始位置也是由连接定位文件所确定,大小在编译连接时自动分配,它和你的程序大小没有关系,但和程序使用到的全局变量,常量数量相关。
②未初始化段(又称全局未初始化段、静态未初始化段) .bss
通常是指用来存放程序中未初始化的全局变量的一块内存区域,在程序载入时由内核清0。bss段属于静态内存分配。它的初始值也是由用户自己定义的连接定位文件所确定,用户应该将它定义在可读写的ram区内,源程序中使用malloc分配的内存就是这一块,它不是根据data大小确定,主要由程序中同时分配内存最大值所确定,不过如果超出了范围,也就是分配失败,可以等空间释放之后再分配。
3、只读数据段 .rodata
程序中定义的常量字符串等
4、堆栈段
栈主要保存函数的局部变量和参数以及返回值。是一种“后进先出”(last in first out,lifo)的数据结构,在调用函数或过程后,系统通常会清除栈上保存的局部变量、函数调用信息及其它的信息。栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低
堆保存函数内部动态分配内存,堆是“先进先出”(first in first out,fifo)数据结构。堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。

所以变量所在段的情况如下:

未初始化全局变量 -----> 数据段—> 未初始化段 .bss
未初始化静态变量 -----> 数据段—> 未初始化段 .bss
初始化全局变量 -----> 数据段—> 初始化段 .data
初始化静态变量 -----> 数据段—> 初始化段 .data
代码 -----> 代码段
常量字符串 -----> 只读数据段 .rodata
非静态局部变量、形参、返回值 -----> 不存在,程序运行时才会在栈区产生
动态申请的变量 -----> 不存在,程序运行时才会在堆区产生

为了验证上述结果,我们写一下程序测试:

#include <stdio.h>

int a;
int b = 1;
static int c;
static int d = 2;

int main(int argc, char *argv[])
{
	  int e;
	  int f = 3;
	  static int g;
	  static int h = 4;
	  char i[] = "hello";
	  //打印每个变量的值
	  printf("a=%d\n",a);
	  printf("b=%d\n",b);
	  printf("c=%d\n",c);
	  printf("d=%d\n",d);
	  printf("e=%d\n",e);
	  printf("f=%d\n",f);
	  printf("g=%d\n",g);
	  printf("h=%d\n",h);
	  printf("i=%s\n",i);
	  //打印每个变量的地址
	  printf("a=%p\n",&a);
	  printf("b=%p\n",&b);
	  printf("c=%p\n",&c);
	  printf("d=%p\n",&d);
	  printf("e=%p\n",&e);
	  printf("f=%p\n",&f);
	  printf("g=%p\n",&g);
	  printf("h=%p\n",&h);
	  printf("i=%p\n",i);
  return 0;
}

编译(不链接,链接会产生更多的数据,不便验证):
#gcc -c test.o test.c
查看数据分段情况:
#objdump -D test.o在这里插入图片描述

编译链接并运行查看打印地址情况:
#gcc -o test tset.c
#./test
在这里插入图片描述

总结

内存分区及数据段分配图
在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值