这里写目录标题
一、关于进程的内存空间划分
程序是代码编译链接的产物,是存储在硬盘上的文件,程序是静止的。
运行起来的程序就是进程,是内存中运行的程序,也就是把程序加载到了内存中,是运行的。
CPU(中央处理器)不能直接访问硬盘,但能直接访问内存。程序必须加载到内存中(变成了进程)才能真正运行起来。
但有些时候会把进程也叫程序,主要是为了沟通方便。
一个程序可以有多个进程。
进程的内存空间划分为以下几个部分:
1代码区-用于存放代码(函数)的区域,特点是只读区。函数指针就是指向代码区的地址
2全局区-用于存放全局变量(定义函数外的变量)和static的局部变量
3BSS段-用于存放未初始化的全局变量的区域。
4栈区(堆栈区stack)-用于存放局部变量的区域,包括:函数的参数和非static的局部变量。系统自动管理栈区内存。
5堆区(heap)- 也叫自由区,程序员唯一可以控制的区域。通常内存分 配、回收都是在堆区malloc()、free()都是在堆区。堆区的内存内存,系统完全不管,程序员全权管理堆区内存。
6只读常量区-存放字符串字面值(即""括起来的字符串)和const修饰的全局变量。这个区域也是只读区,很多书都是把只读常量区并入代码区。
只读常量区的一个例子:
char* s1 = "abc";
s1[0] = '1';//这是不可以的,因为"abc"是字符串字面值
编程示例:
include <stdio.h>
include <stdlib.h>
int i1 = 10;//全局
int i2;//BSS
static int i3 = 10;//全局
const int i4 = 10;//只读
void fa(int i5){
//fa在代码区,i5在栈区
int i6;//栈
const int i7 = 10;//栈
static int i8 = 10;//全局
int* pi = malloc(4);//堆区
char* s1 = "acb";//指向只读常量区。也就是s1一直指向"abc",不可修改
char s2[] = "abc";//指向栈,s2对应着一片存储区而不是只读区,
//存储区里面是数组类型的变量是可以修改的。
//char* s1 = "acb"、char* s2 = "acb";s1和1s2一个地址,这是因
//为只读区里只会存一个"abc"
//char s1[] = "acb"、char s2[] = "acb";s1和1s2不一个地址
printf("i5=%p,i6=%p,i7=%p,i8=%p,s1=%p,s2=%p\n,pi=%p\n",&i5,&i6,&i7,&i8,s1,s2,pi);
free(pi);
}
int main() {
printf("i1=%p,i2=%p,i3=%p,i4=%p\n",&i1,&i2,&i3,&i4);
return 0;
fa(1);
printf("fa=%p\n",fa);//函数名就是函数指针
}
二、虚拟内存地址空间的机制
UNIX/LInux使用虚拟内存地址的方式管理内存,比如:
int a = 10;
printf("%p\n",&a);//得到的那个地址就是虚拟地址。每个进程先天都有0 - 4G-1的虚拟内存地址空间,本质上就是整数(编号)。虚拟内存地址本身不能存储任何的数据,必须映射到物理内存或硬盘上的文件后才能存储数据,否则引发段错误。
对虚拟地址空间举个例子,你买房买了2单元402房,房子还没建好,等真正房子建好了才能住进去。
这里补充一下,为啥是4G-1的空间:
32位系统最多能有多少个内存地址?4G个
2的10次方 -> 1024 -> 1k
2的20次方 -> 10241024 -> 1M
2的30次方 -> 10241024*1024 -> 1G
2的32次方 -> 4G
关于虚拟地址空间和实际物理内存的关系,如下图: