当前位置: 首页 > news >正文

[C语言]编译和链接

一、翻译和运行

在ANSI C的任何⼀种实现中,存在两个不同的环境。
1)翻译环境:将源代码被转换为可执行的机器指令
2)执行环境:实际执行代码。

1.翻译环境

首先我们来将翻译环境
翻译环境是由 编译 链接 两个大的过程组成的,而编译又可以分解成:预处理(有些书也叫预编
译)、编译、汇编三个过程。
vs中的编译器叫:cl.exe
一个C语言的项目中可能有多个 .c 文 件⼀起构建,那多个 .c 文 件如何生成可执行程序呢?
• 多个.c森件单独经过编译出编译处理⽣产对应的目标文件。
• 注:在Windows环境下的目标文件的后缀是 .obj ,Linux环境下目标文件的后缀是 .o
• 多个目标文件和链接库⼀起经过链接器处理⽣成最终的可执行程序。
• 链接库是指运行时库(它是⽀持程序运行的基本函数集合)或者第三方库。

2.运行环境 

程序必须载⼊内存中。在有操作系统的环境中:一般来说,程先是被操作系统载入系统中。在独立的系统中,程序的载入叶可能是通过可执行代码置入只读内存来完成。

程序运行开始,接着便调用main函数。

操作系统开始执行程序代码。这个时候程序将使用一个运行时堆栈,用来存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储与静态内存中的变量在程序的整个执行过程中一直保留它们的值。

终止程序。操作系统正常终止main函数,也有可能是意外终止的。

二、翻译

1.预处理(预编译)

在预处理阶段,源文件和头文件会被处理成为.i为后缀的文件
预处理阶段主要处理那些源文件中#开始的预编译指令。比如:#include,#define
处理的规则如下:
• 将所有的 #define 删除,并展开所有的宏定义。
• 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。
• 处理#include 预编译指令,将包含的头文件的内容插⼊到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。
• 删除所有的注释
• 添加行号和文件名标识,方便后续编译器生成调试信息等。
• 或保留所有的#pragma的编译器指令,编译器后续会使用。
经过预处理后的.i文件中不再包含宏定义,因为宏已经被展开。并且包含的头文件都被插入到.i文件
中。所以当我们五法知道宏定义或者头文件是否包含正确的时候,可以查看预处理后的.i⽂件来确认。

2. 编译

编译过程就是将预处理后的文件进行一系列的: 词法分析 语法分析 语义分析 及优化,生成相应的汇编代码文件

1) 词法分析:

将源代码程序被输⼊扫描器,扫描器的任务就是简单的进行词法分析,把代码中的字符分割成⼀系列的记号(关键字、标识符、字面量、特殊字符等

2)语法分析

接下来语法分析器,将对扫描产生的记号进行语法分析,从而产生语法树。这些语法树是以表达式为 节点的树。

3)语义分析

由语义分析器来完成语义分析,即对表达式的语法层面分析。编译器所能做的分析是语义的静态分 析。静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息。

3.汇编

汇编器是将汇编代码转转变成机器可执行的指令,每⼀个汇编语句几乎都对应一条机器指令。就是根据汇编指令和机器指令的对照表一一的进行翻译,也不做指令优化。

4.链接

链接是⼀个复杂的过程,链接的时候需要把⼀堆文件链接在⼀起才生成可执行程序。
链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。
链接解决的是⼀个项目中多文件、多模块之间互相调用的问题
举个例子:
这里我们创建了两个.c文件如下:test.c和add.c
//test.c
#include <stdio.h>
extern int Add(int x, int y);
extern int aw;
int main()
{int a = 10;int b = 20;int sum = Add(a, b);printf("%d\n", sum);return 0;
}​
//add.c
int aw = 2022;
int Add(int x, int y)
{return x+y;
}
我们已经知道,每个源文件都是单独经过编译器处理生成对应的目标文件。
test.c 经过编译器处理生成 test.o
add.c 经过编译器处理生成 add.o
我们在 test.c 的文件中使用了 add.c 文 件中的 Add 函数和aw 变量。
我们在 test.c 文 件中每⼀次使用  Add 函数和 aw 的时候必须确切的知道 Add aw  的地址,但是由于每个森件是单独编译的,在编译器编译 test.c 的时候并不知道 Add 函数和 aw 变量的地址,所以暂时把调用 Add 的指令的目标地址和 aw  的地址搁置。等待最后链接的时候由链接器根据引用的符号 Add 在其他模块中查找 Add 函数的地址,然后将 test.c 中所有引用到 Add 的指令重新修正,让他们的目标地址为真正的 Add 函数的地址,对于全局变量 aw  也是类似的方法来修正地址。
这个地址修正的过程也被叫做:重定位

本篇我们简要概述了编译和链接,希望对看到的小伙伴有帮助。

相关文章:

  • git提交报错:remote: Please remove the file from history and try again.
  • What is `StringEscapeUtils.escapeHtml4` does?
  • 深入理解 Kubernetes Ingress:路由流量、负载均衡和安全性配置
  • ChatGPT Plus续费充值,到账延迟,如何申诉?
  • 23款奔驰C260L升级原厂360全景影像高清环绕的视野
  • element plus 可选择树形组件(el-tree) 怎样一键展开/收起?实现方法详解
  • Spring Boot多环境配置及Logback日志记录
  • protobuf学习日记 | 认识protobuf中的类型
  • PS为动态GIF添加新背景
  • 广东省第三届职业技能大赛“网络安全项目”B模块--数字取证解析
  • GAN在图像数据增强中的应用
  • 图像处理中常用的距离
  • 【Docker】在centos中安装nginx
  • 电商API接口的大数据分析与挖掘技巧
  • HTML中div内容垂直居中显示
  • 分享一款快速APP功能测试工具
  • Apache Pulsar 2.1 重磅发布
  • crontab执行失败的多种原因
  • dva中组件的懒加载
  • Facebook AccountKit 接入的坑点
  • Java教程_软件开发基础
  • JAVA之继承和多态
  • JDK9: 集成 Jshell 和 Maven 项目.
  • vue 配置sass、scss全局变量
  • 基于webpack 的 vue 多页架构
  • 离散点最小(凸)包围边界查找
  • 深度解析利用ES6进行Promise封装总结
  • 学习HTTP相关知识笔记
  • 一些css基础学习笔记
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • #include
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (Python) SOAP Web Service (HTTP POST)
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (六)vue-router+UI组件库
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (一)kafka实战——kafka源码编译启动
  • (转)mysql使用Navicat 导出和导入数据库
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET的数据绑定
  • .NET是什么
  • @hook扩展分析
  • @JsonSerialize注解的使用
  • @RequestMapping用法详解
  • [Android]一个简单使用Handler做Timer的例子
  • [android学习笔记]学习jni编程
  • [C/C++]数据结构 栈和队列()