《程序员的自我修养》学习笔记——揭秘源文件到可执行文件的编译过程【第一弹】

程序代码到可执行程序编译链接过程

预编译

以c++/c 语言为例,预编译阶段的工作有以下几点:

  1. 处理所有#define 及条件预编译指令(如 #if,#ifdef.....),并展开所有宏定义。
  2. 删除所有注释("//" ,"/**/")。
  3. 处理 "#include",将被包含文件插入该预编译指令位置。(整过过程递归进行,因为被包含文件也可能包含其他文件)
  4. 添加行号与文件标识。(用于调试时产生的编译错误及报错等信息)
预编译过程相当于如下命令:

gcc -E hello.c -o hello.i  (-E 表示只进行预编译)
或者
cpp hello.c > hello.i
编译

编译过程可以分为如下步骤:

image

  1. 扫描

  2. 词法分析

    ​ 运用一种类似于有限状态机的算法,将源代码的字符序列分割为一系列记号(关键字、标识符、字面常量、特殊符号等)。【一个名叫lex的程序可以完成这项任务】

  3. 语法分析

    ​ 对由扫描器产生的记号进行语法分析,进而产生语法树。(采用上下文无关的语法分析手段)【同样一个叫做yacc的工具也可完成这项任务】

  4. 语义分析

    ​ 包括静态语义(如声明和类型的匹配、类型的转化等)和动态语义(运行阶段才能确定)。

  5. 源代码优化【这阶段也包括中间代码(例如llvm 中的 IR)的生成】

    ​ 由于直接在语法树上作优化难度较大,源代码优化器通常将语法树转化为中间代码,再进行优化。

  6. 目标代码生成和目标代码优化

    ​ 代码生成器将中间代码转化成目标机器代码。

    ​ 接着目标代码优化器对上述目标代码进行优化。(如选择合适的寻址方式,删除多余指令等)

编译过程相当于如下命令:
gcc -S hello.i -o hello.s (.s 是汇编输出文件的后缀)
或者
gcc -S hello.c -o hello.s  (预编译和编译合并了)

汇编

汇编器将汇编代码转变为机器可以执行的指令。(生成可重定位文件 .o)

编译过程相当于如下命令:
as  hello.s -o hello.o 
或者
gcc -c hello.s -o hello.o 
或者
gcc -c hello.c -hello.o (上面三个过程一步完成)

链接

对于一个复杂的软件,将每个源代码模块独立地翻译,然后组装。这个组装模块的过程就是链接。(主要包括地址和空间分配、符号决议、重定位等步骤)

最基本的静态链接过程:每个模块的源代码文件(如.c)文件经过编译器编译成可重定位文件(Object File,扩展名为.o或.obj),可重定位文件和库一起链接形成最终可执行文件(.out)。

image

链接过程相当于如下命令:

gcc  hello.o -o hello.out 
以如下代码为例:

#include<stdio.h>

int main()
{
printf("hello world");
return 0;
}
预编译(hello.i) 编译(hello.s)
image image
汇编(hello.o) 链接(hello.out)
image image

可重定位文件 [.o 或 .obj]

可重定位文件的格式

目前PC平台流行的可执行文件格式(Executable)主要是:

PE(Windows)和 ELF(Linux)。【两者都发源自 COFF 可执行文件格式】

另外的如ios 是 Mach-O格式android 是dex格式。

而可重定位文件是源代码编译后但未进行链接的中间文件。(Windows 下的.obj 和 Linux 下的.o)。

因此,可重定位文件和可执行文件的内容和结构是很相似的。(可以广义的将二者看作一种类型的文件)

同时动态链接库(Windows 下的.dll 和 Linux 下的.so)和 静态链接库(Windows 下的.lib 和 Linux 下的.a)文件都可按照可执行文件格式存储。

【小技巧: Linux 下可使用file命令查看相应的文件格式】

程序的指令和数据分开存放的好处:

  1. 程序装载后,数据和指令分别映射到两个虚存区域。数据区域对进程而言是可读写的,指令区域对于进程而言是只读的。这样可以防止程序指令被有意或者无意地更改。
  2. 利于提高程序的局部性。(提高缓存的命中率)
  3. 当系统中运行着多个该程序副本时,内存中只需要保存一份该程序的指令部分。(最重要的原因)

原文链接:https://www.cnblogs.com/Only-xiaoxiao/p/17174043.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:《程序员的自我修养》学习笔记——揭秘源文件到可执行文件的编译过程【第一弹】 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • C 程序 递归函数反转给定的数字

    下面是 “C 程序 递归函数反转给定的数字” 的完整使用攻略。 什么是递归函数? 递归函数是一种在函数体内调用自身的函数,这个过程被称为递归。使用递归函数可以编写简洁而优美的代码。 程序简介 此程序旨在使用递归函数反转给定的数字。例如,如果给定数字为 12345,程序将返回 54321。 使用方法 以下是使用此程序的步骤。 1. 确保您已经安装了 C 语言编…

    C 2023年5月9日
    00
  • springmvc HttpServletRequest 如何获取c:forEach的值

    要获取c:forEach的值,需要进行以下步骤: 1. 在JSP页面中使用c:forEach标签 首先,在JSP页面中使用c:forEach标签遍历集合数据。例如: <c:forEach items="${userList}" var="user"> <tr> <td>${user.…

    C 2023年5月23日
    00
  • Android App调试内存泄露之Cursor篇

    Android App调试内存泄露之Cursor篇 什么是内存泄露 Android应用程序中常见的问题是内存泄漏问题。内存泄漏指的是程序中的对象在使用完之后仍然被占用并未得到垃圾回收,导致内存空间不断被占满,从而引发ANR和崩溃等问题。 Cursor泄露的原因 在Android开发中,我们使用Cursor对象进行数据的操作。Cursor对象是一种轻量级的数据…

    C 2023年5月23日
    00
  • SpringBoot参数校验Validator框架详解

    完整攻略:“SpringBoot参数校验Validator框架详解” 一、介绍 SpringBoot是一个非常流行的轻量级Java开发框架,提供了很多便利的功能以及简洁的语法,使得开发者可以更加快速的进行开发。而参数校验也是开发者在开发过程中必须要面对的一项工作,为了保证程序的正确性,一些基本的参数校验是非常必要的。SpringBoot提供了一套非常方便的参…

    C 2023年5月23日
    00
  • C++实现学生住宿管理系统

    C++实现学生住宿管理系统攻略 系统介绍 学生住宿管理系统主要功能是管理学生住宿信息,包括学生的基本信息和住宿信息,如宿舍楼、宿舍号、床位号等。该系统可以实现学生住宿信息的增删改查等基本操作,方便学生和管理员进行管理。 系统设计 数据库设计 首先,我们需要设计一个数据库,用来存储学生信息和住宿信息。可以使用MySQL或SQLite等关系型数据库,也可以使用文…

    C 2023年5月23日
    00
  • C语言实现小型工资管理系统

    下面我会详细讲解一下“C语言实现小型工资管理系统”的完整攻略。 1. 确定功能需求 首先需要确定工资管理系统的具体功能需求,例如: 添加员工信息 修改员工信息 查询员工信息 删除员工信息 计算员工工资 2. 建立数据存储模型 其次需要建立数据存储模型,决定使用何种数据结构来存储员工信息。通常可以使用结构体来定义员工信息,例如: struct Employee…

    C 2023年5月23日
    00
  • Go Java算法之Excel表列名称示例详解

    Go Java算法之Excel表列名称示例详解 概述 Excel中的列名称由大写字母组成,从”A”开始,到”Z”为止,然后是”AA”,”AB”…依此类推。本文将详细说明如何将数字转换为相应的Excel表列名称。 算法分析 可将数字转换为Excel表列名称的基本思路是将10进制数转换为26进制数,并将每个数字转换为相应的大写字母。 将给定数字减1,以便找出…

    C 2023年5月22日
    00
  • C语言数据的存储超详细讲解中篇练习

    我会为你详细讲解“C语言数据的存储超详细讲解中篇练习”的完整攻略。 攻略概述 “C语言数据的存储超详细讲解中篇练习”主要是讲解C程序中变量和数组的内存模型,以及指针和函数在内存中的存储方式等。该练习主要包含以下部分: C语言中的内存模型 变量和数组的内存模型 指针在内存中的存储方式 函数在内存中的存储方式 示例练习题 在学习这篇练习时,你将会获得对C语言内存…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部