深入了解C++:底层编译原理

简介: 深入了解C++:底层编译原理

进程的虚拟空间划分

任何编程语言,都会产生两样东西,指令和数据。

.exe程序运行的时候会从磁盘被加载到内存中,但是不能直接加载到物理内存中。Linux会给当前进程分配一块空间,比如x86 32位linux环境下会给进程分配2^32(4G)大小的空间,这个空间被叫做【进程的虚拟地址空间】,进程的虚拟地址空间其实并不存在,从底层来看它不过是内核创建的一系列数据结构而已。

以x86 32位linux为例,讲解进程的虚拟地址空间:这部分空间默认首地址是0x00000000,尾地址是0xFFFFFFFF,以0xCCCCCCCC为界被划分为两块,0x00000000~0xCCCCCCCC是【用户空间】,0xCCCCCCCC~0xFFFFFFFF是【内核空间】。

用户空间被进一步划分,0x00000000~0x08048000是不允许访问的空间。往后就是.【text段】,用于存储程序的机器代码(即已编译的指令),其中还有一部分是,rodata段(readonly data),用于存放只读数据(比如char *p=“hello”,所以不能修改)。

再往下就是.data段(专门存放初始化并且初始化不为0的数据),和.bss段(存放未初始化和初始化为0的数据),.bss段会自动把放入的数据做0初始化。最终生成的可执行文件就已经包含了./data段和./bss段里的数据了。

再往下就是.head段,俗称堆内存。

再往下就是共享库空间,用于加载.dll,.so等文件。

再往下就是stack段,俗称栈空间。栈是从高地址往低地址增长,堆是从低地址往高地址增长。栈空间在运行到相应的指令的时候才会被使用。

最后存放命令行参数和环境变量。

内核空间被划分为ZONE_DMA(16M左右),ZONE_NORMAL(800M左右,存放了各种控制块,比如tcb),ZONE_HIGHMEM(地址映射用)

注意:每一个进程用户空间是私有的,内核空间是共享的!所以进程之间通信需要通过内核空间。

#include<stdio.h>
//可执行文件展开的时候
//这些数据都被直接放入相应的./data或./bss段
int gdata1 = 0; //.data
int gdata2 = 0;//.bss
int gdata3;//.bss
static int gdata4 = 1; //.ata
static int gdata5 = 0;//.bss
static int gdata6;//.bss
int main(){
        //编译的时候产生的是指令,
        //可执行问价展开的时候放在.text中,
        //指令运行的时候才会再栈上开辟空间
        int a =12;
        int b =0;
        int c;
        //同.data或.bss
        static int c =13;
        static int f =0;
        static int g;
        return 0;
}

函数调用堆栈空间的过程

下面用一段很简单的代码说明栈堆调用过程。

#include <iostream>
using namespace std;
int sum(int a,int b)
{
  int temp = 0;
  temp = a + b;
  return temp;
}
int main()
{
  int a = 10;
  int b = 20;
  int ret = sum(a,b);
  cout << ret <<endl;
  return 0;
}

我们知道函数运行的时候需要在栈上开辟一块栈桢,.text里的汇编指令运行的时候,会往栈帧里写入相关数据。 比如int a = 10对应的指令运行的时候,会对main函数的栈帧进行压栈,从ebp(栈底)压入了a=10这个数据。

运行上述代码会依次发生以下事情:

1、ebp指针指向main栈帧的栈底,esp指针指向main栈帧的栈顶。esp和ebp指向的是当前调用的栈帧,能表示一块空间。

2、a、b、ret会依次从ebp压入main函数的栈帧中。

3、sum函数的实参会从右往左依次从esp(栈顶压入)。注意,栈帧的大小是会动态变化的,esp会保持位移一直指向栈帧的顶部。

4、把下一指令,也就是sum函数的指令的地址跟从实参地址一起从esp方向压入栈帧中。

5、把main函数调用的地址跟从sum函数调用的地址一起从esp方向压入栈帧中。

6、给sum函数开辟栈帧空间,ebp赋值成esp后,esp指向sum栈帧的顶部。sum函数栈帧可能会被初始化成0xCCCCCCCC。

7、给sum函数的栈帧空间从ebp压入temp。

8、计算a+b的值并赋值给temp空间。

9、回退栈帧,让ebp获取pop出的栈值,所以能重新指向main函数的栈底。

目录
相关文章
|
3月前
|
存储 自然语言处理 算法
【编译原理】LR(1)分析法:C/C++实现
【编译原理】LR(1)分析法:C/C++实现
135 0
|
3月前
|
存储 自然语言处理 算法
【编译原理】逆波兰式的产生及计算:C/C++实现
【编译原理】逆波兰式的产生及计算:C/C++实现
63 0
|
3月前
|
C++
深入了解C++:底层编译原理(二)
深入了解C++:底层编译原理(二)
15 2
|
3月前
|
存储 自然语言处理 编译器
【编译原理】词法分析:C/C++实现
【编译原理】词法分析:C/C++实现
61 1
|
自然语言处理 C++
编译原理 语法分析实验/课程设计(C++实现 附源程序下载)
编译原理 语法分析实验/课程设计(C++实现 附源程序下载)
编译原理 语法分析实验/课程设计(C++实现 附源程序下载)
|
自然语言处理 C++
编译原理 词法分析实验/课程设计C++实现
词法分析阶段是编译过程的第一个阶段,是编译的基础。这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。词法分析程序实现这个任务
编译原理 词法分析实验/课程设计C++实现
|
4天前
|
设计模式 安全 算法
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
15 0
|
6天前
|
C语言 C++
【C++】string类(常用接口)
【C++】string类(常用接口)
15 1