编译到底是在干什么

编译到底是在干什么

写c++都知道,写完程序要编译才能形成可执行文件,那么,编译到底是在干一件什么样的事呢?

编译的例子

写一个简单的helloworld.cpp程序

#include<iostream>
using namespace std;
int main()
{
        cout<<"hello world!\n";
}

然后执行g++进行编译并运行:

[root@VM-238-167-centos /]# g++ -o helloworld helloworld.cpp
[root@VM-238-167-centos /]# ./helloworld 
hello world!

实际上,执行g++ -o这个命令,是进行了非常多的步骤的,包括预处理、编译、汇编、链接

编译与链接

在这里插入图片描述

预处理

预处理过程主要是处理那些以#开头的预编译指令,包括#include或者#define

可以执行g++ -E生成预处理后的文件,后缀为-i

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

查看生成的-i就可以知道将头文件等进行了预编译展开

主要过程如下:

  • 将所有的#define删除,并展开所有的宏定义
  • 处理所有条件预编译指令,包括#if、#ifdef、#else等
  • 处理#include预编译指令,将被包含的文件插入到预编译指令的位置(递归进行)
  • 过滤注释
  • 添加行号和文件名标识
  • 保留所有的#pragma指令

故如果想查看对应的宏编译是否正确展开时,可以查看预编译后的文件

编译与汇编

编译的过程是进行词法分析、语法分析、语义分析以及优化后产生相应的汇编代码文件。

g++ -S helloworld.i -o helloworld.s

执行后查看该文件可以知道已经产生了对应的汇编文件:
在这里插入图片描述

整个编译过程,就是将高级语言翻译为机器语言的过程:通常将其分为6步,包括扫描、词法分析、语义分析、源代码优化、代码生成、目标代码优化。
在这里插入图片描述

编译完成后生成了汇编代码,然后编译器将汇编代码转化为机器码,即产生二进制文件

链接

链接的主要任务是将编译好的各个模块进行一个组合,使得各个模块之间能够进行正确衔接。链接的过程主要是:地址和空间的重分配、符号决议、重定位等步骤。

链接分为静态链接和动态链接:

  • 静态链接:对函数库的链接是放在编译时完成的,所有相关的目标文件与牵涉到的函数库被链接合成到一个可执行文件,故函数执行时与库函数再无瓜葛,所有需要的函数都复制到了相关的位置。也就是说,静态链接将所有用到的函数全部链接到exe文件中
  • 动态链接:把对库函数的链接载入推迟到程序运行时期。在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统中找。某个程序在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝了。如果有,则让其共享那一个拷贝;只有没有才链接载入。

相比之下,静态链接库和动态链接库的特点:

  • 动态链接库更有利于进程间的资源共享
  • 动态链接库让程序的升级变得简单。静态库的升级必须重新编译,而动态库的话只要提供给程序的接口没变,则直接用新生成的动态库替代静态库就行
  • 动态链接库更加节省内存
  • 由于静态库在编译时就将库函数装载到程序中了,动态库是运行时装载,因此用静态库程序运行时速度可能快一些
### 编译原理中 Select 集的作用 在编译原理中,Select 集用于解决自上而下的语法分析过程中可能出现的不确定性问题。当多个产生式的左部相同(即形如 \( A \rightarrow α | β \)),通过计算这些产生式的 Select 集可以判断是否存在冲突以及如何正确解析输入字符串。 具体来说,对于每一个非终结符 \( A \),其对应的每条规则都拥有各自的 Select 集合。如果两条不同规则之间的 Select 集交集为空,则说明这两条规则互不干扰,在遇到相应前缀时能够唯一确定应该应用哪一条规则来继续推导;否则就可能存在二义性,无法构成 LL(1) 文法[^2]。 为了更清晰地理解这一点,考虑如下简单的例子: 假设有一个文法 G: \[ S → aA|bB \\ A → c \\ B → d \\] 此时需要分别求得各产生式的 FIRST 和 FOLLOW 集,并进一步得到 SELECT 集: - 对于 `S -> aA` 的情况,由于左侧第一个符号 'a' 属于终端字符,因此直接将其加入到该产生式的 SELECT 集中; - 同理可处理其他几个产生式,最终获得完整的 SELECT 表达形式。 ```python # Python伪代码表示获取SELECT集合的过程 def get_select_sets(grammar_rules, first_set, follow_set): select_sets = {} for rule in grammar_rules: lhs, rhs = rule.split('->') if rhs[0].isupper(): # 如果右侧首字母是非终结符 temp_first = set() for symbol in rhs: if symbol.islower(): break temp_first |= first_set[symbol] if '#' not in first_set[symbol]: continue if '#' in temp_first: temp_first.remove('#') temp_first |= follow_set[lhs.strip()] select_sets[rule] = temp_first else: # 终结符开头的情况 select_sets[rule] = {rhs[0]} return select_sets ``` 此过程有助于确保所构建的预测分析表无多重定义项,从而使得给定文法成为有效的 LL(1) 类型文法[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值