C语言程序编译与链接(拓宽视野的不二之选)

2024-03-28 02:12

本文主要是介绍C语言程序编译与链接(拓宽视野的不二之选),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 翻译环境和运行环境
    • 翻译环境
    • 预处理
    • 编译
    • 汇编
    • 链接
  • 运行环境

翻译环境和运行环境

1,在ANSI C的任何⼀种实现中,存在两个不同的环境。

第1种是翻译环境,在这个环境中源代码被转换为可执⾏的机器指					令(⼆进制指令)。
第2种是执⾏环境,它⽤于实际执⾏代码。

在这里插入图片描述

翻译环境

那翻译环境是怎么将源代码转换为可执⾏的机器指令的呢?这⾥我们就得展开开讲解⼀下翻译环境所做的事情。

其实翻译环境是由编译和链接两个⼤的过程组成的,⽽编译⼜可以分解成:预处理(有些书也叫预编译)、编译、汇编三个过程。

在这里插入图片描述

⼀个C语⾔的项⽬中可能有多个 .c ⽂件⼀起构建,那多个 .c ⽂件如何⽣成可执⾏程序呢?

  • 多个.c⽂件单独经过编译器,编译处理⽣成对应的⽬标⽂件。
  • 注:在Windows环境下的⽬标⽂件的后缀是 .obj ,Linux环境下⽬标⽂件的后缀是 .o
  • 多个⽬标⽂件和链接库⼀起经过链接器处理⽣成最终的可执⾏程序。
  • 链接库是指运⾏时库(它是⽀持程序运⾏的基本函数集合)或者第三⽅库。

在这里插入图片描述

预处理

在C语言中,预处理阶段是编译过程中的第一步,主要是通过预处理器对源代码进行处理,包括宏替换、头文件包含、条件编译等操作。下面详细解释一下预处理阶段的几个重要概念和操作:

  1. 头文件包含(Include Directives):
    #include 指令:用于包含其他文件的内容,分为尖括号包含系统头文件(如#include <stdio.h>)和双引号包含用户定义的头文件(如#include “myheader.h”)。

  2. 宏替换(Macro Replacement):
    宏定义:使用#define指令定义一个宏,如#define PI 3.14159。
    宏替换:预处理器会在编译前将代码中出现的宏名称替换为对应的值,比如将代码中的PI替换为3.14159。

  3. 条件编译(Conditional Compilation):
    条件编译指令:如#if、#ifdef、#ifndef、#elif、#else、#endif等,用于根据条件选择性地编译代码块。

  4. 其他预处理指令
    #undef:取消已定义的宏。
    #ifdef 和 #ifndef:判断某个宏是否已经定义。
    #error:在预处理时生成一个错误信息。
    #pragma:向编译器发出特定指令。

预处理器工作流程
1,将源文件中的头文件包含进来。
2,对源文件进行宏替换。
3,处理条件编译指令,根据条件编译部分代码。
4,生成一个经过预处理的中间文件,后缀为.i,供后续编译阶段使用。
5,删除所有的注释
6, 添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等

经过预处理后的.i⽂件中不再包含宏定义,因为宏已经被展开。并且包含的头⽂件都被插⼊到.i⽂件
中。所以当我们无法知道宏定义或者头⽂件是否包含正确的时候,可以查看预处理后的.i⽂件来确认。

编译

  1. 词法分析(Lexical Analysis):
    目的
    将源代码按照词法规则分割成单词(Token)序列。

    工作内容
    识别关键字、标识符、常量、运算符等单词,并生成对应的标记(Token)。生成标记流(Token Stream)作为下一步的输入。
    请添加图片描述

  2. 语法分析(Syntax Analysis):
    目的:
    将标记流转换成抽象语法树(Abstract Syntax Tree,AST)或语法分析树。

    工作内容:
    根据语法规则检查标记流是否符合语言语法规范。
    构建抽象语法树,表示源代码的结构和语法。

请添加图片描述

  1. 语义分析(Semantic Analysis):
    目的:
    进行语义检查,确保程序的语义正确。

    工作内容:
    检查类型匹配、变量的定义和使用是否正确。
    解析表达式,计算常量表达式的值。
    检查函数调用、返回值等语义正确性。

在这里插入图片描述

  1. 中间代码生成(Intermediate Code Generation):
    目的:
    将抽象语法树转换成中间代码表示。

    工作内容:
    生成一种中间表示形式,如三地址码、四元式等。
    将高级语言的结构转换成更加容易进行优化的形式。

  2. 优化(Optimization):
    目的:
    对中间代码进行优化,提高程序的执行效率。

    工作内容:
    利用各种优化技术,如常量传播、死代码删除、循环优化等,提高程序性能。
    生成更加高效的中间代码表示,以便后续的代码生成阶段使用。

  3. 代码生成(Code Generation):
    目的:
    将优化后的中间代码转换成目标机器代码。

    工作内容:
    根据目标机器的特性和指令集,将中间代码转换为机器指令。
    处理寄存器分配、指令选择等问题,生成最终的目标代码。

汇编

当将C语言代码转换为汇编语言时,主要涉及到编译器将高级语言代码翻译成等效的汇编语言代码。以下是详细介绍汇编语言的步骤:

  1. 指令表示
    汇编语言使用助记符(Mnemonics)来代表特定的机器指令,如mov用于数据传送、add用于加法运算等。
  2. 寄存器
    计算机有一组寄存器用于存储数据和执行操作,如通用寄存器(如eax、ebx)、数据寄存器(如edx)、地址寄存器(如esi、edi)等。
  3. 内存访问
    使用不同的寻址模式(如立即数偏移、寄存器间接寻址)来访问内存中的数据。
  4. 控制流
    汇编语言提供了跳转指令(如jmp)和条件跳转指令(如je、jne)来控制程序的执行流程。
  5. 过程调用
    使用call来调用函数,使用ret返回函数调用,需要处理函数参数传递和局部变量存储。
  6. 栈操作
    使用栈来保存函数调用过程中的返回地址、参数以及局部变量,通过push和pop指令来操作栈。
  7. 数据处理
    汇编语言提供了各种指令来进行数据处理,如移位指令、逻辑运算指令、算术运算指令等。
  8. 标志寄存器
    标志寄存器记录了运算结果的信息,如进位标志、零标志、符号标志等,影响程序的条件跳转。
    9.宏指令
    汇编语言支持宏定义,可以简化重复代码的书写,提高代码的可读性和维护性。

链接

链接是将多个目标文件(包括库文件)组合成一个可执行文件或动态链接库的过程。以下是链接过程的详细步骤:

  1. 符号解析(Symbol Resolution):
    目的:解析所有目标文件中的符号引用,确定它们对应的实际地址或存储位置。
    工作内容:
    遍历所有目标文件,收集每个符号(如函数名、全局变量名)的定义和引用信息。
    解析外部符号引用,确定这些符号最终在哪个目标文件或库文件中定义。
    2.重定位(Relocation):
    目的:修正目标文件中的相对地址,使其能正确地映射到最终的内存地址。
    工作内容:
    根据符号解析的结果,对所有涉及到的地址进行调整,确保它们能正确地指向符号的实际位置。
    生成包含所有修正地址的重定位表,以便在加载时进行修正。
  2. 地址空间分配(Address Allocation):
    目的:为目标文件中的变量和函数分配内存地址。
    工作内容:
    确定每个全局变量和函数在内存中的起始地址。
    处理重复定义和冲突,确保分配的地址不会发生重叠或冲突。
  3. 符号重命名(Symbol Renaming):
    目的:避免不同目标文件中的符号名字冲突。
    工作内容:
    对于静态链接,可以对不同目标文件中的相同符号进行重命名,以避免冲突。
    对于动态链接,通常使用全局符号表(Global Symbol Table)来管理符号名字,确保唯一性。
  4. 生成可执行文件或动态链接库(Executable/Dynamic Link Library Generation):
    目的:将经过符号解析、重定位等处理后的目标文件转换为最终的可执行文件或动态链接库。
    工作内容:
    将已经修改过的目标文件内容按照特定的格式组合成可执行文件或动态链接库。
    对于可执行文件,可能还需要添加一些运行时所需的信息,如程序入口点等。
  5. 符号表生成(Symbol Table Generation):
    目的:生成最终可执行文件或动态链接库中的符号表,记录符号名字和对应的地址信息。
    工作内容:
    生成包含所有符号信息的符号表,以便在加载时进行符号解析和重定位。

运行环境

1,程序必须载⼊内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独⽴的环境中,程序的载⼊必须由⼿⼯安排,也可能是通过可执⾏代码置⼊只读内存来完成。

2, 程序的执⾏便开始。接着便调⽤main函数。

3,开始执⾏程序代码。这个时候程序将使⽤⼀个运⾏时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使⽤静态(static)内存,存储于静态内存中的变量在程序的整个执⾏过程⼀直保留他们的值。

4,终⽌程序。正常终⽌main函数;也有可能是意外终⽌。

这篇关于C语言程序编译与链接(拓宽视野的不二之选)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/854140

相关文章

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

java程序远程debug原理与配置全过程

《java程序远程debug原理与配置全过程》文章介绍了Java远程调试的JPDA体系,包含JVMTI监控JVM、JDWP传输调试命令、JDI提供调试接口,通过-Xdebug、-Xrunjdwp参数配... 目录背景组成模块间联系IBM对三个模块的详细介绍编程使用总结背景日常工作中,每个程序员都会遇到bu

Go语言中json操作的实现

《Go语言中json操作的实现》本文主要介绍了Go语言中的json操作的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录 一、jsOChina编程N 与 Go 类型对应关系️ 二、基本操作:编码与解码 三、结构体标签(Struc

uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)

《uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)》在uni-app开发中,文件上传和图片处理是很常见的需求,但也经常会遇到各种问题,下面:本文主要介绍uni-app小程序项目中实... 目录方式一:使用<canvas>实现图片压缩(推荐,兼容性好)示例代码(小程序平台):方式二:使用uni

python语言中的常用容器(集合)示例详解

《python语言中的常用容器(集合)示例详解》Python集合是一种无序且不重复的数据容器,它可以存储任意类型的对象,包括数字、字符串、元组等,下面:本文主要介绍python语言中常用容器(集合... 目录1.核心内置容器1. 列表2. 元组3. 集合4. 冻结集合5. 字典2.collections模块

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求:

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Go语言连接MySQL数据库执行基本的增删改查

《Go语言连接MySQL数据库执行基本的增删改查》在后端开发中,MySQL是最常用的关系型数据库之一,本文主要为大家详细介绍了如何使用Go连接MySQL数据库并执行基本的增删改查吧... 目录Go语言连接mysql数据库准备工作安装 MySQL 驱动代码实现运行结果注意事项Go语言执行基本的增删改查准备工作