按32位的Linux系统,对于一个进程,其0-3G的用户空间分布如下图所示:
从低地址到高地址,一般分为:
- 代码段。顾名思义就是一些代码,当然也包括 函数。
- 只读数据段。主要存的是 只读数据,包括字符串常量、初始化的const全局常量,初始化的const static常量。这些值初始化后不能够被改变,即使通过指针也不可以,否则会 出现段错误。
- 初始化数据段。主要存储的是初始化的的非const类型的 全局变量和初始化的 static变量。
- 未初始化数据段。包括未初始化的或初始化为0的全局变量和static变量,包括const修饰的全局变量和static变量。const类型变量可以通过指针改变。未初始化数据都是0。
- 堆。malloc申请的空间。
- 加载动态库区。
- 栈。
- 普通局部变量。
- 指向命令行参数及环境变量的指针。
- 命令行参数和环境变量。
(由于加载动态库区的地址不知道怎么获得,暂且放弃。)下面就用代码显示哪种数据存储在哪个区段,以更好的理解进程空间。建议在32位Linux系统上测试。
#include<stdio.h>
#include<stdlib.h>
int GlobleInit = 10;
int GlobleNoInit;
int GlobleInit0 = 0;
int GlobleNoInArr[10];
int GlobleInArr[10] = {1,2,3};
static int StaticGlobleIn = 3;
static int StaticGlobleNoIn;
const static int CStaticGlobleIn = 2;
const static int CStaticGlobleNoIn;
const int ConstGloble = 1;
const int ConstGlobleNoIn;
int Func(void);
int Func2(int a);
int Func3(int a);
int main(int argc, char *argv[], char *env[])
{
int i = argc-1;
printf("env[%d] = %p %s\n",0,env[0],env[0]);//环境变量
for(; i>=0;i--){ //命令行参数
printf("argv[%d] = %p %s\n",i,argv[i],argv[i]);
}
printf("&env[%d] = %p\n",0,&env[0]); //环境变量
for(i = argc-1; i>=0;i--){ //命令行参数
printf("&argv[%d] = %p\n",i,&argv[i]);
}
printf("&env = %p\n",&env); //环境变量
printf("&argv = %p\n",&argv); //命令行参数
printf("&argc = %p\n",&argc); //命令行参数个数
Func2(2);
register registerPart = 10; //register 变量不能获取地址
const int ConstPart1 = 2; //0xbfc3ce68变量为什么按相反的顺序入桟
int Stack = 38; //0xbfc3ce6c
int StackNoIn; //0xbfc3ce6c
const int ConstPart2 = 2; //0xbfc3ce70
static int StaticPartIn20 = 20;
static int StaticPartIn = 22;
static int StaticPartIn21 = 21;
static int StaticPartNoIn;
static int StaticPartInArr[128] = {1};
static int StaticPartNoInArr[128];
const static int CStaticPartIn = 2;
const static int CStaticGPartNoIn;
int *Heap = (int*)malloc(sizeof(int)*100);
FILE *fp = fopen("aaa","r+");
if(fp == NULL)
return -1;
fgetc(fp);
//局部变量,栈
printf("&ConstPart2 = %p\n",&ConstPart2);
printf("&Stack = %p\t%d\n",&Stack,Stack);
printf("&StackNoIn = %p\t%d\n",&StackNoIn,StackNoIn);
printf("&ConstPart1 = %p\n",&ConstPart1);
//堆
printf("_IO_buf_base = %p\n",fp->_IO_buf_base);
printf("Heap = %p\n",Heap);
//未初始化数据段
printf("&GlobleNoInit = %p\t%d\n",&GlobleNoInit,GlobleNoInit);
printf("&GlobleNoInArr = %p\t[0]=%d\n",GlobleNoInArr,GlobleNoInArr[0]);
printf("&CStaticGPartNoIn = %p\n",&CStaticGPartNoIn);
printf("&ConstGlobleNoIn = %p\n",&ConstGlobleNoIn);
printf("&StaticPartNoIn = %p\n",&StaticPartNoIn);
printf("StaticPartNoInArr = %p\n",StaticPartNoInArr);
printf("&StaticGlobleNoIn = %p\n",&StaticGlobleNoIn);
printf("&CStaticGlobleNoIn = %p\n",&CStaticGlobleNoIn);
printf("&GlobleInit0 = %p\t%d\n",&GlobleInit0,GlobleInit0);
//初始化数据段
printf("StaticPartInArr = %p\n",StaticPartInArr);
printf("&StaticPartIn20 = %p %d\n",&StaticPartIn20,StaticPartIn20);
printf("&StaticPartIn = %p\n",&StaticPartIn);
printf("&StaticPartIn21 = %p %d\n",&StaticPartIn21,StaticPartIn21);
printf("&StaticGlobleIn = %p\n",&StaticGlobleIn);
printf("&GlobleInit = %p\n",&GlobleInit);
printf("&GlobleInArr = %p\t[0]=%d\n",GlobleInArr,GlobleInArr[0]);
//只读数据段
printf("&CStaticPartIn = %p\n",&CStaticPartIn);
printf("\"asdfg\" = %p\n","asdfg");
printf("&ConstGloble = %p\n",&ConstGloble);
printf("&CStaticGlobleIn = %p\n",&CStaticGlobleIn);
//代码段
printf("&Function = %p\n",Func);
printf("&main = %p\n",main);
fclose(fp);
free(Heap);
return 0;
}
int Func(void){
static int StaticPartInFunc = 22;
static int StaticPartNoInFunc;
printf("StaticPartNoInFunc = %p\n",&StaticPartNoInFunc);
printf("StaticPartInFunc = %p\n",&StaticPartInFunc);
}
int Func2(int a){
int PartInFunc = 22;
int PartNoInFunc;
printf("PartNoInFunc = %p\n",&PartNoInFunc);
printf("PartInFunc = %p\n",&PartInFunc);
}
int Func3(int a){
}
将代码编译后生成‘a.out’可执行文件,在用'readelf -S a.out '获得一些内存空间地址的信息,如下:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
... ...
[11] .init PROGBITS 08048370 000370 00002e 00 AX 0 0 4
[13] .text PROGBITS 08048430 000430 00060c 00 AX 0 0 16
[14] .fini PROGBITS 08048a3c 000a3c 00001a 00 AX 0 0 4
[15] .rodata PROGBITS 08048a58 000a58 00032c 00 A 0 0 4
... ...
[24] .data PROGBITS 0804a020 001020 00028c 00 WA 0 0 32
[25] .bss NOBITS 0804a2c0 0012ac 00028c 00 WA 0 0 32
将a.out执行后输出如下:
env[0] = 0xbfa59404 MANPATH=/opt/Qt5.3.1//man:
argv[0] = 0xbfa593fe a.out
&env[0] = 0xbfa5899c
&argv[0] = 0xbfa58994
&env = 0xbfa58908
&argv = 0xbfa58904
&argc = 0xbfa58900
PartNoInFunc = 0xbfa588ac
PartInFunc = 0xbfa588a8
&ConstPart2 = 0xbfa588e0
&Stack = 0xbfa588d8 38
&StackNoIn = 0xbfa588dc -1218888107
&ConstPart1 = 0xbfa588d4
_IO_buf_base = 0xb7718000//fopen函数文件默认缓冲区地址
Heap = 0x8bb0008
&GlobleNoInit = 0x804a548 0
&GlobleNoInArr = 0x804a520 [0]=0
&CStaticGPartNoIn = 0x804a2f0
&ConstGlobleNoIn = 0x804a500
&StaticPartNoIn = 0x804a2f4
StaticPartNoInArr = 0x804a300
&StaticGlobleNoIn = 0x804a2e4
&CStaticGlobleNoIn= 0x804a2e8
&GlobleInit0 = 0x804a2e0 0
.bss 0x804a2c0
StaticPartInArr = 0x804a0a0
&StaticPartIn20 = 0x804a2a0 20
&StaticPartIn = 0x804a2a4
&StaticPartIn21 = 0x804a2a8 21
&StaticGlobleIn = 0x804a088
&GlobleInit = 0x804a040
&GlobleInArr = 0x804a060 [0]=1
.data 0x804a020
&CStaticPartIn = 0x8048d80
"asdfg" = 0x8048cdb
&ConstGloble = 0x8048a64
&CStaticGlobleIn = 0x8048a60
.rodata 0X8048a58
&Function = 0x804891f
&main = 0x80484e4
.text 0X8048430
最后在说说为什么会有未初始化数据段呢?请跳转下面到连接:
C语言全局未初始化数据段分析 http://blog.csdn.net/candcplusplus/article/details/12576185
弱符号与强符号概念 http://www.cnblogs.com/whos/archive/2010/10/20/1856274.html
然后我还要补充一下。未初始化数据段的数据在编译完生成可执行文件后,只是在文件中占了个位置,并没有任何数据在文件中存有真实数据。
#include<stdio.h>
int arr[1000] = {0};
const int glob;
int main(){
return 0;
}
-rwxrwxr-x 1 oracle oracle<span style="font-family: Arial, Helvetica, sans-serif;"> </span>7163 9月 21 19:09 a.out
#include<stdio.h>
int arr[1000] = {1};
const int glob;
int main(){
return 0;
}
-rwxrwxr-x 1 oracle oracle<span style="font-family: Arial, Helvetica, sans-serif;"> </span>11211 9月 21 19:12 a.out
显然位初始化数据的执行文件要小。