c语言在嵌入式、操作系统、图像处理方面应用广泛,是一种比较底层的语言。本文主要介绍c语言的内存分配,进程在内存中的布局。

环境:

Linux zhuzhu 4.2.0-27-generic #32~14.04.1-Ubuntu SMP

gcc version 4.4.7

首先上一张进程在内存中的布局图:



注: 该图仅表示进程在32位linux操作系统下的布局,对于windows内存布局并不是这样的,有兴趣的可以将下面的程序在VC6.0上运行试试,会发现与此布局相差很大。

从图中可以看到:

1、高地址空间0xBFFFFFFF-0xFFFFFFFF为内核空间,用户程序无法直接访问;

2、用于存放环境变量、main函数传进来的参数存放空间;

3、栈空间主要用于存放局部变量、函数参数等,其从高地址向低地址增长;

4、未分配区,主要作用是供栈、堆动态扩展用和mmap映射;

5、主要用于动态内存分配空间,从低地址向高地址增长;

6、BSS用于存放未初始化的全局变量、初始化为零的全局变量;

7、数据段主要用来存放初始化的全局变量,静态全局、局部变量,常量,只读变量;

8、代码段用于存放可执行的代码,为只读。

注: 对于ARM架构来说函数传参,前四个参数是放在R0-R3寄存器中的,超过四个的参数才会放堆区。

例:

#include<stdio.h>#include<stdlib.h>#include<string.h>#defineSTR_SIZE32intg_un_init;intg_init[100000]={0};intg_init_a=521;staticintg_static_data=125;intmain(intargc,char*argv[]){intlocal_a;staticintlocal_static_b;char*str="xiaozhu";char*local_str=NULL;local_str=malloc(STR_SIZE);if(local_str==NULL){printf("nomem\n");return-1;}printf("argv[1]=%p\n",argv[1]);printf("g_un_init=%p\n",&g_un_init);printf("g_init=%p\n",g_init);printf("g_init_a=%p\n",&g_init_a);printf("g_static_data=%p\n",&g_static_data);printf("local_a=%p\n",&local_a);printf("local_static_b=%p\n",&local_static_b);printf("str=%p\n",str);printf("local_str=%p\n",local_str);free(local_str);return0;}

编译程序,对编译结果在ubuntu上执行:readelf -h a.out 来获取程序的入口地址为0x8048390:




程序执行结果为:

root@zhuzhu:blog#./a.outzhuargv[1]=0xbfa80633g_un_init=0x80abae4g_init=0x804a060g_init_a=0x804a028g_static_data=0x804a02clocal_a=0xbfa8025clocal_static_b=0x80abae0str=0x8048640local_str=0x8d0b008


在命令行下执行:readelf -S a.out 获取程序详细段大小:

root@zhuzhu:blog#readelf-Sa.outThereare30sectionheaders,startingatoffset0x117c:SectionHeaders:[Nr]NameTypeAddrOffSizeESFlgLkInfAl[0]NULL0000000000000000000000000[1].interpPROGBITS0804815400015400001300A001[2].note.ABI-tagNOTE0804816800016800002000A004[3].note.gnu.build-iNOTE0804818800018800002400A004[4].gnu.hashGNU_HASH080481ac0001ac00002004A504[5].dynsymDYNSYM080481cc0001cc00007010A614[6].dynstrSTRTAB0804823c00023c00005800A001[7].gnu.versionVERSYM0804829400029400000e02A502[8].gnu.version_rVERNEED080482a40002a400002000A614[9].rel.dynREL080482c40002c400000808A504[10].rel.pltREL080482cc0002cc00002808A5124[11].initPROGBITS080482f40002f400002d00AX004[12].pltPROGBITS0804833000033000006004AX0016[13].textPROGBITS0804839000039000028a00AX0016[14].finiPROGBITS0804861c00061c00001900AX004[15].rodataPROGBITS080486380006380000ad00A004[16].eh_frame_hdrPROGBITS080486e80006e800002400A004[17].eh_framePROGBITS0804870c00070c00009000A004[18].ctorsPROGBITS08049f20000f2000000800WA004[19].dtorsPROGBITS08049f28000f2800000800WA004[20].jcrPROGBITS08049f30000f3000000400WA004[21].dynamicDYNAMIC08049f34000f340000c808WA604[22].gotPROGBITS08049ffc000ffc00000404WA004[23].got.pltPROGBITS0804a00000100000002004WA004[24].dataPROGBITS0804a02000102000001000WA004[25].bssNOBITS0804a040001030061aa800WA0032[26].commentPROGBITS0000000000103000004e01MS001[27].shstrtabSTRTAB0000000000107e0000fc00001[28].symtabSYMTAB0000000000162c0004901029484[29].strtabSTRTAB00000000001abc00025e00001

从上面结果可以看到其代码段、数据段、BSS段的起始地址和长度


结合执行结果可以看出:

argv[1]=0xbfa80633

local_a=0xbfa8025c

和理论布局是对应的,位置大致一致。


g_un_init=0x80abae4g_init=0x804a060local_static_b=0x80abae0

位于BSS段起始地址为0x804a040,大小为0x61aa8,表示未初始化或初始化为零的全局变量位于BSS段,且未初始化的局部静态变量也位于此段。


g_init_a=0x804a028g_static_data=0x804a02

初始化的全局变量和静态全局变量都位于数据段.data。


str=0x8048640

常量字符串位于只读数据段.rodata。


local_str=0x8d0b008

位于堆空间


注: 向只读段、内核空间、未分配区赋值都会引发段错误,如在程序中加入:

*(unsignedint*)0xa0000000=1;

就会引发段错误。


注:要养成malloc与free成对使用的习惯,负责代码量大了,容易内存泄漏。

思考:什么malloc需要指定分配的大小,而free是只需一个地址参数即可?


刚开始写博客,理解不是很深入,希望大家多讨论,共同学习,共同进步。。。