Compiler construction 什么是编译器、链接器、加载程序?
我想深入了解编译器、链接器和加载程序的含义和工作方式。Compiler construction 什么是编译器、链接器、加载程序?,compiler-construction,linker,terminology,loader,Compiler Construction,Linker,Terminology,Loader,我想深入了解编译器、链接器和加载程序的含义和工作方式。 参照任何语言,最好是C++。 < P>维基百科应该有一个好的答案,这是我的想法: 编译器:读取.c源代码,写入.o对象 链接器:将多个*.o文件连接到一个可执行程序中 加载程序:将可执行文件加载到内存并启动其运行的代码 编译器读取、分析代码并将其转换为目标文件或错误消息列表 链接器将一个或多个对象文件和一些可能的库代码组合成一些可执行文件、一些库或错误消息列表 加载程序将可执行代码读入内存,进行一些地址转换,并尝试运行该程序,从而产生正
参照任何语言,最好是C++。 < P>维基百科应该有一个好的答案,这是我的想法:
- 编译器:读取.c源代码,写入.o对象
- 链接器:将多个*.o文件连接到一个可执行程序中
- 加载程序:将可执行文件加载到内存并启动其运行的代码
- 编译器读取、分析代码并将其转换为目标文件或错误消息列表
- 链接器将一个或多个对象文件和一些可能的库代码组合成一些可执行文件、一些库或错误消息列表
- 加载程序将可执行代码读入内存,进行一些地址转换,并尝试运行该程序,从而产生正在运行的程序或错误消息(或两者兼有)
[Source Code] ---> Compiler ---> [Object code] --*
|
[Source Code] ---> Compiler ---> [Object code] --*--> Linker --> [Executable] ---> Loader
| |
[Source Code] ---> Compiler ---> [Object code] --* |
| |
[Library file]--* V
[Running Executable in Memory]
编译器更改检查源代码是否有错误,并将其更改为目标代码。这是操作系统运行的代码 您通常不会在单个文件中编写整个程序,所以链接器会链接所有目标代码文件 您的程序只有在主内存中才能执行* 虽然它是所有其他计算系统的基本概念,但已针对基于linux/unix的系统进行了解释。 * LinuxJournal清晰地解释了这个概念。这也解释了经典的名字a是如何产生的。(汇编程序输出) 一个简短的总结
c程序-->[compiler]-->对象文件-->[linker]-->可执行文件(比如a.out)
我们获得了可执行文件,现在将此文件提供给您的朋友或需要此软件的客户:)
当他们运行这个软件时,比如在命令行中键入。/a.out
在命令行中执行。/a.out-->[Loader]-->[execve]-->程序加载到内存中
一旦程序加载到内存中,通过使PC(程序计数器)指向
a.out
的第一条指令,将控制权转移到此程序。编译器是一种特殊程序,它处理用特定编程语言编写的语句,并将其转换为机器语言或“代码”计算机处理器使用的编译器将编程语言中的代码行翻译成机器语言
链接器在两个程序之间创建链接
加载程序将程序加载到主数据库、程序等的内存中。- 编译器:将人类可理解的格式转换为机器可理解的格式
- 链接器:将机器可理解格式转换为操作系统可理解格式
- 加载程序:实际将程序加载并运行到RAM中的实体
- 静态链接
- 动态链接
有关Linux中程序执行的这三个阶段的详细研究,请参阅 上面的代码将生成解析错误,因为公式不正确 平衡的该单元通过生成解析器树(如下所示)在内部对此进行检查 如下: 因此,这个单元也称为解析器 3) 语义分析器: 本单元检查语句中的含义。例如:
{
int i;
int *p;
p = i;
-----
-----
-----
}
上述代码生成错误“分配不兼容类型”
4) 预优化:
该单元独立于CPU,即有两种类型的优化
- 一) 死码消除
- 二) 子码消除
- 三) 循环优化
{
int a = 10;
if ( a > 5 ) {
/*
...
*/
} else {
/*
...
*/
}
}
在这里,编译器在编译时知道'a'的值,因此
知道if条件始终为真。因此,它消除了else
代码中的一部分
二) 子代码消除:
例如:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = a + b + c;
/*
...
*/
}
{
int a;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
a = 10;
/*
...
*/
}
}
可以按如下方式进行优化:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = x + c; // a + b is replaced by x
/*
...
*/
}
{
int a;
a = 10;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
}
}
三) 循环优化:
例如:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = a + b + c;
/*
...
*/
}
{
int a;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
a = 10;
/*
...
*/
}
}
{
INTA;
对于(i=0;i<1000;i++){
/*
...
*/
a=10;
/*
...
*/
}
}
在上面的代码中,如果“a”是本地的并且没有在循环中使用,那么它可以是
优化如下:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = x + c; // a + b is replaced by x
/*
...
*/
}
{
int a;
a = 10;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
}
}
{
INTA;
a=10;
对于(i=0;i<1000;i++){
/*
...
*/
}
}
5) 代码生成:
在这里,编译器生成汇编代码,以便
常用变量存储在寄存器中
6) 优化后:
这里的优化依赖于CPU。假设有多个
在代码中跳转,然后将其转换为:
-----
jmp:<addr1>
<addr1> jmp:<addr2>
-----
-----
-----
jmp:
jmp:
-----
-----
控件直接跳转到控件
最后一个阶段是链接(创建可执行文件或库)。
运行可执行文件时,将加载它所需的库。编译器:它是一个将高级语言程序转换为机器语言程序的程序。编译器比汇编程序更智能。它检查各种限制、范围、错误等,但它的程序运行时间更长,占用的内存更大。它的速度很慢。因为编译器会遍历整个程序