c程序的编译和链接

author author     2022-08-14     552

关键词:

 本文主要讲述了一个C程序从源代码到目标文件所经过的步骤,介绍了编译系统,预处理、编译、汇编和链接的相关知识。

一、编译系统 

  一个C程序的生命周期从高级C语言程序开始。想要在系统上执行.c程序,每条C语句都必须翻译为低级的机器语言指令,将这些指令按照可执行目标程序的格式打包,以二进制磁盘文件的形式存放,这就是可以由系统执行的可执行目标文件。这些工作包含如下图所示的四个过程,由编译系统完成。

  一般而言,编译系统包括预处理器、编译器、汇编器和链接器。

                    技术分享

   注意一下上图中各个阶段输出的文件格式是文本文件还是二进制文件。

1.预处理阶段

  源代码.c文件和相关的头文件如stdio.h等被预处理器编译为一个.i文件。

预处理过程主要处理那些源代码文件中的以#开头的预处理指令。主要规则如下:

  a.删除所有的#define,并展开所有的宏定义;

  b.处理所有条件预处理指令,如#if #ifdef #elif #else #endif;

  c.处理#include预处理指令,将被包含的文件插入到该预处理指令的位置;

  d.删除所有的注释// /* */;

  e.添加行号和文件名标识,如#2 ‘‘hello.c" 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号;

  f.保留所有的#pragma编译器指令,因为编译器要使用它们。

从上面几条规则我们可以知道:

  a.预处理得到.i文件中,已经将所有宏都展开,不再包含任何宏定义,而且头文件也被插入到相应的位置。

  b.注释信息也是在预处理阶段进行处理的,也就是说构建后得到的目标文件不包含注释内容,源文件中添加多少注释对最后的文件的大小没有影响。

  c.编译报错时的行号信息和文件标识也是在这里产生的。

2.编译阶段

  预处理之后得到.i文件,再对.i文件进行编译,得到.s文件。

  编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后产生相应的汇编代码文件。汇编程序中的每条语句都以一种标准的文本格式确切地描述了一条低级机器语言指令。

3.汇编阶段

  .s文件汇编之后得到.o文件,即目标文件。

  汇编器将汇编代码转变成机器可以执行的指令,并把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件中。

  .o文件是二进制文件,其字节编码是机器语言指令。

4.链接阶段

  链接器将相关的.o文件合并,得到可执行目标文件。可执行文件能够被加载到内存中,由系统执行。

 

二、编译器的工作

编译过程如下图所示,一般可以分为6步:扫描、语法分析、语义分析、源代码优化、代码生成和目标代码优化。

                    技术分享

1.词法分析

  记号(Token)一般可以分为如下几类:关键字、标识符、字面量(数组、字符串等)和特殊符号(加减号、等号等)。

  词法分析的工作由扫描器(Scanner)完成,它利用类似于有限状态机的算法将源代码的字符序列分割成一系列的记号,同时将标识符放到符号表,将字面量放到文字表等。

2.语法分析

  语法分析器(Grammer Parser)采用用上下文无关语法(Context-free Grammar)对记号进行语法分析,产生语法树(Syntax Tree)。许多运算符号的优先级和含义也在这个阶段确定,如圆括号优先级比乘法高,*表示乘法还是对指针取内容。

  语法树就是以表达式(Expression)为节点的树。

  如果发现表达式不合法,编译器会报错。

3.语义分析

  语法分析仅仅完成了对表达式的语法分析,但并不了解表达式的含义。

  语义分析器(Semantic Analyzer)完成语义分析。

  注意编译器只能做静态语义(Static Semantic)分析,就是在编译器可以确定的语义,包括声明和类型的匹配、类型的转换。语义分析后,语法树的表达式会被标识类型。

  动态语义(Dynamic Semantic)只有在运行期才能确定,编译器不能确定。

4.中间语言生成

  对代码进行优化时,源码级优化器(Source Code Optimizer)会在源代码上进行优化,但直接在语法树上做优化比较困难,因此源码级优化器将语法树转换为中间代码(Intermediate Code)。

  中间代码是语法树的顺序表示,它接近目标代码,但是它不包含数据的尺寸、变量地址和寄存器的名字等,所以跟目标机器和运行时环境无关。

  中间代码类型有许多,常见的有三地址码和P-代码。

5.目标代码的生成与优化

  生成中间代码之后的过程,就属于编译器后端了。编译器后端主要包括代码生成器(Code Generator)和目标代码优化器(Target Code Optimizer)。

  代码生成器将中间代码转换为目标机器代码,这个过程要确定字长、寄存器、整数数据类型和浮点数类型等,因此十分依赖于目标机器。

  目标代码优化器对目标代码进行优化,如确定寻址方式、删除多余指令和用位移代替乘法运算等操作。

 

  经过这些步骤,源代码被编译为目标文件。

  这里需要考虑一个问题,如果源代码所需的变量名、函数名等都在同一个编译单元里,编译器可以确定它们的地址。但是如果有些变量名或函数名定义在其他的程序模块里,该如何处理?

  事实上,定义在其他模块的全局变量和函数在最终运行时的绝对地址都是在链接的时候确定下来的。

三、链接器的作用

  一个程序被分为很多模块,模块之间如何通信呢?

  静态语言C模块之间的通信有两种方式,即函数调用和变量访问。函数调用需要知道目标函数的地址,变量访问要知道目标变量的地址,这两种方式归结起来就是模块间符号的引用。而这个工作就由链接器完成。

  链接程序的主要工作就是将每个源代码独立编译后得到的目标文件连起来,即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有目标文件能正确地衔接,成为一个能够由操作系统装入执行的统一整体。

  链接过程主要包括了地址和空间分配、符号决议和重定位等步骤。

  连接方式有两种:静态链接和动态连接。在后续文章中进行详细的介绍。

 

c/c++程序编译过程详解

C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系... 查看详情

使用 QT-Creator 编译和链接 C++ 和 C 程序

】使用QT-Creator编译和链接C++和C程序【英文标题】:CompileandlinkC++andCprogrammstogetherwithQT-Creator【发布时间】:2016-01-2417:55:25【问题描述】:所以我有多个C++文件,基本上需要在我的程序的C++部分完成后调用一个C函数。因此,如果... 查看详情

c语言——程序环境和预处理

程序的翻译环境和执行环境编译+链接预处理一.程序的翻译环境和执行环境在ANSIC标准的任何一种实现中,存在两种不同的环境:翻译环境:该环境中源代码会被转换为可执行的机器指令 执行环境:其用于实际执行代码二.编... 查看详情

cgo编译和链接参数(代码片段)

CGO编译和链接参数编译和链接参数是每一个C/C++程序员需要经常面对的问题。构建每一个C/C++应用均需要经过编译和链接两个步骤,CGO也是如此。本节我们将简要讨论CGO中经常用到的编译和链接参数的用法。编译参数:CFLAGS/CPPFLAG... 查看详情

如何正确编译和链接 gdbus 程序

】如何正确编译和链接gdbus程序【英文标题】:Howtoproperlycompileandlinkagdbusprogram【发布时间】:2020-12-2020:53:16【问题描述】:我正在尝试在Linux上学习dbus和程序的编译/链接。我对从头开始构建和链接应用程序相当陌生。为此,我... 查看详情

使用静态库链接、编译和运行 c 程序

】使用静态库链接、编译和运行c程序【英文标题】:linking,compilingandrunningacprogramwithastaticlibrary【发布时间】:2017-02-2600:10:00【问题描述】:我是C开发新手。A我在CLion中构建了一个库(静态)图书馆.h#ifndefMYLIB_LIBRARY_H#defineMYLIB_LIB... 查看详情

c语言中程序的编译(预处理操作)+链接详解(详细介绍程序预编译过程)(代码片段)

...包含4.3嵌套文件包含5.结尾1.前言今天我们来学习C语言中程序的编译和链接是如何进行的。在ANSIC的任何一种实现中,存在两个不同的环境。第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第2种是执... 查看详情

c_cpp一个加载着色器文件,编译和链接到着色器程序的函数。(代码片段)

查看详情

为啥 C 程序会在运行时针对 C++ 库编译和链接 C 编译器然后 SIGILL?

】为啥C程序会在运行时针对C++库编译和链接C编译器然后SIGILL?【英文标题】:WhywouldaCprogramcompileandlinkwithaCcompileragainstC++librariesthenSIGILLatruntime?为什么C程序会在运行时针对C++库编译和链接C编译器然后SIGILL?【发布时间】:2016-01-... 查看详情

写了一个程序可以编译c语言,怎么自动再链接然后执行生成的可执行文件?

我编写了一个程序,可以对该目录下的某个特定名称的c程序编译生成汇编程序,经验证可行。现在想问的是如何改进程序,不仅可以编译,还可以进行链接等操作,再执行生成的二进制文件?是否需要用到cmd脚本,这个脚本可以... 查看详情

c语言篇——程序的编译(代码片段)

今天我来补一下C语言篇的程序的编译的一篇文章,也算是有一个结尾了。目录程序的翻译环境和执行环境编译和链接翻译环境编译的几个阶段预处理编译汇编链接运行环境程序的翻译环境和执行环境在ANSIC的任何一种实现中&... 查看详情

c语言篇——程序的编译(代码片段)

今天我来补一下C语言篇的程序的编译的一篇文章,也算是有一个结尾了。目录程序的翻译环境和执行环境编译和链接翻译环境编译的几个阶段预处理编译汇编链接运行环境程序的翻译环境和执行环境在ANSIC的任何一种实现中&... 查看详情

c程序编译过程浅析

前几天看了《程序员的自我修养——链接、装载与库》中的第二章“编译和链接”,主要根据其中的内容简单总结一下C程序编译的过程吧。我现在一般都是用gcc,所以自然以GCC编译hellworld为例,简单总结如下。 hello.c源代码... 查看详情

c程序编译过程浅析

...:http://blog.csdn.net/koudaidai/article/details/8092647前几天看了《程序员的自我修养——链接、装载与库》中的第二章“编译和链接”,主要根据其中的内容简单总结一下C程序编译的过程吧。我现在一般都是用gcc,所以自然... 查看详情

gcc和g++的区别解析

...代码,只是细节不同,后缀名为.c的源文件,gcc将其当作C程序,而g++则当作c++程序来处理;后缀名为.cpp的源文件,gcc和g++都会当作C++程序来处理。编译阶段,g++会调用gcc来进行编译,但由于gcc不能链接程序所使用的库,所以需... 查看详情

程序的静态链接(代码片段)

程序的静态链接程序的产生程序是由程序员编写,经过编译链接过程,最终能够在计算机中运行的东西。本质上来说编译链接过程其实就是将由人能看懂的代码段翻译成机器能看懂的代码段,然后指导机器的运行,比起程序在机... 查看详情

刨析c语言的程序环境

C语言程序运行的环境1、程序的翻译环境和执行环境2、详解编译+链接编译过程链接过程翻译过程详解3、运行环境1、程序的翻译环境和执行环境在ANSIC的任何一种实现中,存在两个不同的环境。第1种是翻译环境,在这... 查看详情

刨析c语言的程序环境

C语言程序运行的环境1、程序的翻译环境和执行环境2、详解编译+链接编译过程链接过程翻译过程详解3、运行环境1、程序的翻译环境和执行环境在ANSIC的任何一种实现中,存在两个不同的环境。第1种是翻译环境,在这... 查看详情