《编译与反编译技术》—第1章1.7节C语言程序的编译流程


本节书摘来自华章出版社《编译与反编译技术》一书中的第1章,第1.7节C语言程序的编译流程,作者庞建民,陶红伟,刘晓楠,岳峰,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.7 C语言程序的编译流程

本节以C语言程序的编译流程为例,介绍实际的C语言编译器是如何运作的。通常把整个代码的编译流程分为编译过程和链接过程。

1.编译过程

编译过程可分为编译预处理、编译与优化、汇编等阶段。

(1)编译预处理

编译预处理即读取C源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。主要包括以下几个方面:

1)宏定义指令,如# define Name TokenString、# undef等。对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,但作为字符串常量的Name则不被替换。对于后一个伪指令,则将取消对某个宏的定义,使以后该串的出现不再被替换。

2)条件编译指令,如# ifdef、# ifndef、# else、# elif、# endif等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

3)头文件包含指令,如# include "FileName"或者# include <FileName>等。在头文件中一般用伪指令# define定义了大量的宏,还有对各种外部符号的声明。采用头文件的目的是使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条# include语句,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入它所产生的输出文件中,以供编译程序对之进行处理。注意,这个过程是递归进行的,也就是说,被包含的文件可能还包含其他文件。包含到C源程序中的头文件可以是系统提供的,这些头文件一般放在/usr/include目录下,在# include中使用它们要用尖括号(<  >)。另外开发人员也可以定义自己的头文件,这些文件一般与C源程序放在同一目录下,此时在# include中要用双引号(" ")。

4)特殊符号。例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义与没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输入而被翻译成为机器指令序列。

5)删除注释。删除所有的注释“//…”和“/*…*/”。

6)保留所有的#pragma编译器指令。以#pragma开始的编译器指令必须保留,因为编译器需要使用它们。

经过预编译后的.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入.i文件中。所以,当无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定。

(2)编译与优化

经过预编译得到的输出文件中只有常量、变量的定义,以及C语言的关键字,如main、if、else、for、while、{、}、+、-、*、\等。编译程序所要做的工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。优化处理涉及的问题不仅同编译技术本身有关,而且同机器的硬件环境也有关。优化中的一种是对中间代码的优化。另一种优化则主要是针对目标代码的生成而进行的。对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除等。后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑如何充分利用机器的各个硬件寄存器存放有关变量的值,以减少对内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是优化的一个重要任务。经过优化得到的汇编代码序列必须经过汇编程序的汇编转换成相应的机器指令序列,方能被机器执行。

(3)汇编

汇编过程是把汇编语言代码翻译成目标机器指令的过程。对于待编译处理的每一个C语言源程序,都将经过这一处理过程而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的机器语言代码。目标文件由段组成,通常一个目标文件中至少有两个段:①代码段。该段中所包含的主要是程序的机器指令,一般是可读和可执行的,但却不可写。②数据段。主要存放程序中要用到的各种全局变量或静态的数据,一般是可读、可写、可执行的。

UNIX环境下主要有三种类型的目标文件:①可重定位文件,其中包含适合于其他目标文件链接以创建一个可执行的或者共享的目标文件的代码和数据。②共享的目标文件,这种文件存放了适合于在两种上下文里链接的代码和数据。第一种是静态链接程序,可把它与其他可重定位文件共享的目标文件一起处理来创建另一个目标文件;第二种是动态链接程序,将它与另一个可执行文件及其他共享目标文件结合到一起,创建一个进程映像。③可执行文件,它包含了一个可以被操作系统通过创建一个进程来执行的文件。汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这就是链接程序的工作了。

2.链接过程

由汇编程序生成的目标文件并不能立即被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经过链接程序的处理方能得以解决。链接程序的主要工作就是将有关的目标文件彼此相连接,亦即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。根据开发人员指定的与库函数的链接方式的不同,链接处理通常可分为两种:①静态链接。在该方式下,函数的代码将从其所在的静态链接库中被复制到可执行程序中。这样当该程序被执行时,这些代码将被装入该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。②动态链接。在该方式下,函数的代码被放到称作动态链接库或共享对象的某个目标文件中。链接程序此时所做的只是在最终的可执行程序中记录下共享对象的名字以及一些少量的登记信息。在该可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越,在某些情况下动态链接可能带来一些性能上的

损失。

3. GCC的编译链接

在Linux中使用的GCC编译器是把以上几个过程进行了捆绑,使用户只使用一次命令就完成编译工作,这确实很方便,但对于初学者了解编译过程却很不利。GCC代理的编译流程如下:①预编译,将.c文件转化成.i文件,使用的GCC命令是gcc –E (对应于预处理命令cpp);②编译,将.c/.h文件转换成.s文件,使用的gcc命令是gcc –S(对应于编译命令cc1,实际上,现在版本的GCC使用cc1将预编译和编译两个步骤合成为一个步骤;③汇编,将.s文件转化成.o文件,使用的GCC命令是gcc –c (对应于汇编命令as);④链接,将.o文件转化成可执行程序,使用的GCC命令是gcc(对应于链接命令ld)。

以名为hello.c的程序为例,编译流程主要经历如图1-2所示的四个过程。

 

图1-2 C语言程序编译流程图

7b391cb6f5e27995b756d792d0556036cb2853d0

例如,hello.c为:

#include <stdio.h>

Int main(int argc,char *argv[])

{

    printf("hello world\n");

    return 0;

}

运行gcc –S hello.c可以得到hello.s文件,其内容为:

.file "hello.c"

.def  ___main; .scl  2; .type 32; .endef

.section  .rdata,"dr"

LC0:

.ascii "hello world\0"

.text

.globl _main

.def _main; .scl  2; .type 32; .endef

_main: 

LFB6: 

.cfi_startproc 

pushl   %ebp 

.cfi_def_cfa_offset 8

  ...

所有以字符“.”开头的行都是指导汇编器和链接器的命令,其他行则是被翻译成汇编语言的代码。

C语言编译的整个过程是比较复杂的,涉及的编译器知识、硬件知识、工具链知识非常多。一般情况下,只需要知道其分成编译和链接两个阶段,编译阶段是将源程序(*.c)转换成为目标代码(一般是obj文件),链接阶段是将源程序转换成的目标代码(obj文件)与程序里面调用的库函数对应的代码链接起来形成对应的可执行文件(exe文件),其他的都需要在实践中多多体会才能有更加深入的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Base64

2019独角兽企业重金招聘Python工程师标准>>> Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64&#xff0c;所以每6个比特为一个单元&#xff0c;对应某个可打印字符。三个字节有24个比特&#xff0c;对应于4个Base64单元&#xff0c…

软件配置管理(六)常用配置软件配置工具指令

文章目录软件配置管理工具的主要功能两种版本控制模型Lock-Modify-UnlockCopy-Modify-MergeSubversionGit软件配置管理工具的主要功能 版本控制变更管理配置审核配置状态统计问题跟踪访问控制和安全控制 两种版本控制模型 Lock-Modify-Unlock “加锁-修改-解锁”模型 对于版…

.NET 6 攻略大全(一)

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;15分钟)欢迎使用 .NET 6。今天的版本是.NET 团队和社区一年多努力的结果。C# 10 和 F# 6 提供了语言改进&#xff0c;使您的代码更简单、更好、性能大幅提升&#xff0c;我们已经看到微软降低了托管云服务的成本。.NE…

Win7下JDK环境变量的设置

JDK并不像Microsoft阵营vs那样智能&#xff0c;安装好后所有的东西都给你配置好了&#xff0c;我们还没需要手动配置很多东西 首先说为什么要配置JDK的环境变量在任何路径下识别java命令和java类 配置分为2个部分&#xff0c;1&#xff0c;java命令路径。2,java加载类 分为3个步…

简单模拟实现简单的当登录延时的效果

①、先建立一个activaty去部署我们的登陆界面 1 package com.example.administrator.actionbardemo;2 3 import android.app.Activity;4 import android.content.Intent;5 import android.os.Bundle;6 import android.view.View;7 import android.widget.Button;8 import andro…

《音乐达人秀:Adobe Audition实战200例》——实例11 录制任意音量音乐

本节书摘来自异步社区《音乐达人秀&#xff1a;Adobe Audition实战200例》一书中的第2章&#xff0c;实例11 &#xff0c;作者 健逗&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 实例11 录制任意音量音乐 这个实例将讲解如何任意控制录音过程中的音量。你…

我的技术回顾2019不止技术的一年

这篇本想具体说下ABP的商业模式的演进与变化&#xff0c;因为愿意像我这样花费心力去研究他的人不多。写着的时候&#xff0c;就得没必要。真有这种想法的人&#xff0c;也会自己研究&#xff0c;我何必去写呢。2019年初&#xff0c;因为各种原因&#xff0c;加上企业资源的调整…

linux之在当前目录下删除不包含aa的文件

1、问题 1、当前目录下删除不包含aa的文件和文件夹2、解决办法 1、rm -rf ls -al | grep -v "aa"

设计模式(1)--简单工厂模式、策略模式

设计模式&#xff08;1&#xff09;--简单工厂模式、策略模式 1. 简单工厂模式 在阎宏博士的《JAVA与模式》一书中开头是这样描述简单工厂模式的&#xff1a;简单工厂模式是类的创建模式&#xff0c;又叫做静态工厂方法&#xff08;Static Factory Method&#xff09;模式。简单…

kmp算法讲解

转自——http://blog.csdn.net/v_july_v/article/details/7041827 看到kmp是不是立即想到(*ο*) 哇&#xff5e;&#xff0c;那个东西啊&#xff0c;就是拿来放电影的那个啊&#xff01; 哦&#xff0c;但是这里我们说的并不是那个东西&#xff0c;身为一名C选手&#xff0c;我…

TLS/SSL协议工作原理

1、TLS/SSL的功能实现原理简单介绍 HTTPS协议的主要功能基本都依赖于TLS/SSL协议,TLS/SSL的功能实现主要依赖于三类基本算法:散列函数 Hash、对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的…

实现DDD领域驱动设计: Part 4

原文链接: https://dev.to/salah856/implementing-domain-driven-design-part-iv-29m2对象到对象映射当两个对象具有相同或相似的属性时&#xff0c;自动对象到对象映射是一种将值从一个对象复制到另一个对象的有用方法。DTO和实体类通常具有相同/相似的属性&#xff0c;你通常…

软件项目组织管理(六)项目时间管理

文章目录项目计划和进度计划什么是活动活动清单活动属性里程碑时间管理的过程1. 活动定义2. 活动排序三种依赖关系箭线图法&#xff08;ADM&#xff09;/双代号网络&#xff08;AOA&#xff09;前导图法&#xff08;PDM&#xff09;/单代号网络&#xff08;AON&#xff09;3. 活…

win7开机按F8后,为什么没有修复计算机的选项

有时win7开机按F8后&#xff0c;没有修复计算机的选项本文将向大家介绍如何通过 Windows 7自带的“配置 Windows 恢复环境(RE)”命令 – ReagentC.exe&#xff0c;启用“高级启动选项”下的“修复计算机”选项。单击 Windows 开始图标&#xff0c;在“搜索程序和文件”输入框中…

TLS/SSL握手过程

参考了:https://www.wosign.com/faq/faq2016-0309-04.htm 1、握手与密钥协商过程 基于RSA握手和密钥交换的客户端验证服务器为示例详解TLS/SSL握手过程。 (1).client_hello 客户端发起请求,以明文传输请求信息,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数,扩…

FluentValidation在C#中是怎么进行数据验证的

介绍FluentValidation 是一个基于 .NET 开发的验证框架&#xff0c;用于构建强类型验证规则的 .NET 库。开源免费支持 .Net所有平台 包括.NetFramework和.NetCore.FluentValidation 组件内提供十几种常用验证器&#xff0c;可扩展性好&#xff0c;支持自定义验证器&#xff0c;…

基于Metronic的Bootstrap开发框架经验总结(5)--Bootstrap文件上传插件File Input的使用...

Bootstrap文件上传插件File Input是一个不错的文件上传控件&#xff0c;但是搜索使用到的案例不多&#xff0c;使用的时候&#xff0c;也是一步一个脚印一样摸着石头过河&#xff0c;这个控件在界面呈现上&#xff0c;叫我之前使用过的Uploadify 好看一些&#xff0c;功能也强大…

软件项目组织管理(七)项目成本管理

文章目录什么是成本什么是项目成本学习曲线理论储备金什么是项目成本管理目的过程成本估算成本估算的工具和技术成本预算主要工作成本估算和成本预算的关系和区别成本管理的工具方法净现值投资回报率挣值分析法什么是成本 为达到一个特定目标而花费的资源。 什么是项目成本 …

大话领域驱动设计——简介

如果说当下最热门的技术概念或架构思想&#xff0c;那么领域驱动设计&#xff08;DDD&#xff09;一定占有一席之地。上个系列&#xff0c;我讲了ABP vNext框架在微服务架构下的落地思路&#xff0c;而ABP vNext是基于DDD思想的完整框架之一&#xff0c;同时DDD也是微服务架构服…

软件项目组织管理(八)项目质量管理

文章目录软件质量的重要性对质量的认识传统的认识新的认识质量与等级什么是质量什么是质量管理什么是软件质量软件项目管理的目标质量管理的过程质量管理发展的四个阶段戴明改进循环&#xff08;PDCA循环&#xff09;项目质量计划编制方法——质量标杆法影响项目质量的因素(5M1…