P1 C++如何从源文件变为可执行文件

前言

欢迎来到 C ++系列的新章节,今天我们要学习 C ++是如何工作的。现阶段我们尽量简单点说,学习如何从源文件开始,也就是实际的文本文档到可执行的二进制代码的过程。


对于C++源文件,从文本到可执行文件一般需要四个过程:

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接


一 预处理阶段:

现有两文件main.cpp 和 log.h,然后将文件预处理生成main.i

main.c源文件

//#include <iostream>   
#include "log.h"    //头文件
#define my_INT int   //预处理后替换后被删除
/* 注释 观察预处理后是否存在*/
my_INT main()
{
    my_INT data = 8;
    log();
    return 0;
}

log.h源文件

#pragma

void log()
{
    int log = 5;
}

用g++编译器将源文件进行编译,生成main.i文件

g++ -E main.cpp  -o main.i

  1. 对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,成预编译文件。
  2. 删除所有的#define,展开所有的宏定义。
  3. 处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。
  4. 处理“#include”预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其他文件。
  5. 删除所有的注释,“//”和“/**/”。
  6. 保留所有的#pragma 编译器指令,编译器需要用到他们,如:#pragma once 是为了防止有文件被重复引用。
  7. 添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告是能够显示行号。


二 编译阶段:

将预处理好的main.编译为汇编文件,汇编文件为.s文件

g++ -S main.i -o main.s
  1. 词法分析:利用类似于“有限状态机”的算法,将源代码程序输入到扫描机中,将其中的字符序列分割成一系列的记号。
  2. 语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的语法树是一种以表达式为节点的树。
  3. 语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义——在编译期能分期的语义,相对应的动态语义是在运行期才能确定的语义。
  4. 优化:源代码级别的一个优化过程。
  5. 目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列——汇编语言表示。
  6. 目标代码优化:目标代码优化器对上述的目标机器代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余的指令等。


三 汇编阶段:

g++ -c main.s -o main.o

  1. 功能:.o是GCC生成的目标文件,除非你是做编译器和连接器调试开发的,否则打开这种.o没有任何意义。二进制机器码一般人也读不了。
  2. 将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件。
  3. 汇编器的汇编过程相对于编译器来说更简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译过来,汇编过程有汇编器as完成。经汇编之后,产生目标文件(与可执行文件格式几乎一样)xxx.o(Linux下)、xxx.obj(Windows下)。


四 链接阶段:

将多个目标文件及所需要的库连接成最终的可执行目标文件。链接分为静态链接和动态链接:

静态链接:

  • 函数和数据被编译进一个二进制文件。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件。
  • 空间浪费:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本;
  • 更新困难:每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。
  • 运行速度快:但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。

2、动态链接: 

  • 动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。
  • 共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;
  • 更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。
  • 性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。

 

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@ChenPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值