C语言编译与链接(29)

news2024/12/27 15:50:59

文章目录

  • 前言
  • 一、翻译环境和执行环境
  • 二、翻译环境
    • 预处理(预编译)
    • 编译
      • 词法分析
      • 语法分析
      • 语义分析
    • 汇编
    • 链接
  • 三、运行环境
  • 总结


前言

  本篇同样是一篇修炼内功的文章
  很重要!它将会让你对程序的构建运行有一个更深的认识


一、翻译环境和执行环境

在ANSI C的任何一种实现中,存在两个不同的环境:

  1. 翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令
  2. 执行环境,它用于实际执行代码

在这里插入图片描述

在本篇中,我们会来详细介绍一下翻译环境

二、翻译环境

翻译环境是由编译和链接两个大过程组成的,将源代码转换为可执行的机器指令,编译又可以分解成:预处理(预编译)、编译、汇编三个过程
在这里插入图片描述

再简要一点,就像如下图
在这里插入图片描述

其中,几个.c文件经过编译器的处理,生成了几个.obj文件就叫做编译器,它们再与链接库一起经过链接器处理生成可执行程序.exe,这就叫做链接

具体来说,多个 .c 文件如何生成可执行程序呢?
在我细说之前,你先得有以下认识:

  1. 多个.c文件单独经历编译处理生产对应的目标文件(windows环境下后缀为.obj,Linux环境下后缀为.o)
  2. 多个目标文件和链接库一起经过链接器处理生成可执行程序
  3. 链接库是指运行时库(支持程序运行的基本库函数集合)或者第三方库

然后就跟我一起从头开始,构建一个完整的程序的过程吧~

预处理(预编译)

在预处理阶段,源文件和头文件会被处理成为.i为后缀的文件

此阶段主要处理那些源文件中#开始的预编译指令,例如#include,#define

具体规则如下:

  1. 将所有的 #define 删除,并展开所有的宏定义
  2. 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif
  3. 处理#include预编译指令,将包含的头文件的内容插⼊到该预编译指令的位置
  4. 这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件
  5. 删除所有的注释
  6. 添加行号和文件名标识,方便后续编译器生成调试信息等,或保留所有的#pragma的编译器指令,编译器后续会使用

如果想在gcc环境下观察,可以输入以下命令行:
gcc -E test.c -o test.i

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

如该图,我们发现#include <stdio.h>被展开,竟然有好几百行!且添加了文件名标识
至于上述其他规则特性,就交由大家自行验证了
在这里插入图片描述

编译

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

指令为:
gcc -S test.i -o test.s

假设这个时候来了个语句:

nums[index] = (index + 4) * (2 + 6);

词法分析

将源代码程序被输入扫描器,扫描器的任务就是简单的进行词法分析,把代码的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等。)
在这里插入图片描述

语法分析

将对扫描产生的记号进行语法分析,从而产生语法树(以表达式为节点的树)
在这里插入图片描述

语义分析

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

到这里,程序的后缀已经变成.s了
在这里插入图片描述

汇编

由汇编器将汇编代码转为机器指令(二进制指令),每一句汇编语句几乎都对应一条机器指令。按照汇编指令和机器指令的对照表进行一一的进行翻译,不做指令优化

指令为:
gcc -c test.s -o test.o

然后你会发现看不了,看不了就对了,就算能看也是乱码
在这里插入图片描述

链接

将一堆文件链接在一起生成可执行程序,为解决一个项目中多文件、多模块之间互相调用的问题,主要过程:地址和空间分配,符号决议和重定位等步骤,事实上,这很复杂,我也只是粗略点拨一下

现在我们来个具体例子,现在在一个C的项目里面,有两个.c文件(main.c、add.c)

//test.c
#include <stdio.h>

//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;

int main()
{
	int a = 10;
	int b = 20;
	int sum = Add(a, b);
	
	printf("%d\n", sum);
	
	return 0;
}
//add.c
int g_val = 2024;

int Add(int x, int y)
{
	return x + y;
}

现在我们开始运行程序,首先通过前文我们知道,两个.c文件经过编译后生成两个.o目标文件,也就是说

test.c 经过编译器处理生成 test.o
add.c 经过编译器处理生成 add.o

  而我们在 test.c 的文件中使用了 add.c 文件中的 Add 函数和 g_val 变量,在 test.c 文件中每⼀次使用 Add 函数和 g_val 的时候必须确切的知道 Add 和 g_val 的地址(是的,函数也有地址,这在前面讲过),但是由于每个文件是单独编译的,在编译器编译 test.c 的时候并不知道 Add 函数和 g_val变量的地址,所以暂时把调用 Add 的指令的目标地址和 g_val 的地址搁置。

  等待最后链接的时候由链接器根据引用的符号 Add 在其他模块中查找 Add 函数的地址,然后将 test.c 中所有引用到Add 的指令重新修正,让他们的目标地址为真正的 Add 函数的地址,对于全局变量 g_val 也是类似的方法来修正地址

这个地址修正的过程也被叫做:重定位

事实上,我第一次知道链接是这样子运行的时候,我还是蛮激动的,这太巧妙了,不得不感叹计算机殿堂里先辈们的智慧

  我对这个过程有个还算贴切的比方,假如小举和小帆是好兄弟,小举年纪到了要买房子,首付三十万,此时手里只能拿出二十五万,剩下五万,要找小帆借,小帆答应了,小举就去签字了
  这时候,买房子就相当于是要调用Add函数和g_val变量,可是在自己的.c文件没有,对应差五万,找小帆借,小帆答应了,对应着声明,可是声明还需要有定义,此时还只是口头承诺,若另一个文件真的有声明,就能修正为正确的地址,对应小举真的拿到钱,另一种情况则是由于某些原因小帆无法借钱,就像是另一个文件里没有定义,这就导致之前错误的地址无法修正,承诺未能兑现,程序出错,小帆也就交不了首付了,Bad Ending~

三、运行环境

  可能你会觉得编译的过程好难啊,或者我讲得好浅啊!

那你感觉对了,我在这里只是讲了点皮毛,大学计科对于编译有一门专门的课程叫做《编译原理》
这可能是计算机类专业最无聊、相对来说最难的一门课了~

那我们接着来讲运行环境吧,在这里我就讲些有关注意点吧:

  1. 程序必须载入内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排(嵌入式的烧录),也可能是通过可执行代码置入只读内存来完成
  2. 程序的执行便开始接着便调用main函数
  3. 开始执行程序代码。这个时候程序将使用⼀个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程⼀直保留他们的值
  4. 终止程序。正常终止main函数;也有可能是意外终止

总结

在这里插入图片描述

  来个总览,本篇应该能让你有些启发,下篇我们会对预处理那一部分再详细介绍一下

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

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

相关文章

[uni-app]小兔鲜-08云开发

uniCloud可以通过JS开发服务端,包含云数据库, 云函数, 云存储等功能, uniCloud可结合 uni-ui 组件库使用 效果展示: <picker>城市选择组件不支持h5端和APP端, 所以我们使用 <uni-data-picker>组件进行兼容处理 <uni-data-picker>的数据使用云数据库的数据 云…

指针(7)

目录 1. sizeof和strlen的对⽐ 1.1 sizeof 1.2 strlen sizeof 和 strlen 总结&#xff1a; 2. 数组和指针 2.1 ⼀维数组 2.2 字符数组 1. sizeof和strlen的对⽐ 1.1 sizeof 计算的是使⽤类型创建的变量所占内存空间的⼤⼩。sizeof不在乎你里面放的什么。sizieof是操作符…

基于springboot人力资源管理系统源码

项目技术&#xff1a;SpringBoot 运行环境&#xff1a;jdk1.8idea/eclipsemaven3mysql5.6 项目描述&#xff1a; 系统包括&#xff0c;员工管理&#xff0c;奖惩管理&#xff0c;合同管理&#xff0c;薪酬管理&#xff0c;培训管理&#xff0c;绩效评估等功能

x++、++x的一些问题

x、x在字面上无非就说一个先前置递增然后再运算&#xff0c;另一个是运算完再递增&#xff0c;是不是有些许模棱两可的感觉&#xff0c;接下来引用一个简单的for循环就能够大致理解&#xff1a; 先是x&#xff1a; int i0,x0;for(i0;(i)<5;){xi;printf("%d\n",x)…

影刀RPA实战:Excel排序、替换与格式

1.实战目标 今天继续介绍影刀RPA操作Excel的指令&#xff0c;内容替换&#xff0c;数据排序与单元格格式设置&#xff0c;这几个功能在日常工作中使用率还是比较频繁的。我们可以使用影刀来处理这些重复繁琐的工作。 2.内容替换 我们手动替换内容时 打开Excel文件&#xff1…

【Spring】“请求“ 之后端传参重命名,传递数组、集合,@PathVariable,@RequestPart

1. 后端传参重命名&#xff08;后端参数映射&#xff09; 某些特殊情况下&#xff0c;前端传递的参数 key 和我们后端接收的 key 可以不一致&#xff0c;比如前端传了一个 time 给后端&#xff0c;而后端是使用 createtime 字段来接收的&#xff0c;这样就会出现参数接收不到的…

第二十二天|回溯算法| 理论基础,77. 组合(剪枝),216. 组合总和III,17. 电话号码的字母组合

回溯算法理论基础 1.题目分类 2.理论基础 什么是回溯算法 回溯和递归是相辅相成的。 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 回溯法的效率 回溯法其实就是暴力查找&#xff0c;并不是什么高效的算法。 因为回溯的本质是穷举&#xff0c;穷举所有可…

销冠的至高艺术:让自己不像销售

若想在销售领域脱颖而出&#xff0c;首先是让自己超越传统销售的框架&#xff0c;成为客户心中不可多得的行业顾问与信赖源泉。这不仅是身份的蜕变&#xff0c;更是影响力与信任度质的飞跃。 销冠对客户只吸引不骚扰&#xff0c;不讲自己卖什么&#xff0c;只讲自己能解决什么…

销售秘籍:故事+观点+结论

在销售的浩瀚宇宙中&#xff0c;隐藏着一个不朽的秘诀——利用人类共有的“错失恐惧”&#xff0c;激发客户内心的渴望与行动。正如村上春树所言&#xff0c;每个故事都深深植根于灵魂&#xff0c;而大仲马则揭示&#xff0c;灵魂之眼所见&#xff0c;比肉眼更为长久铭记。 错…

【C++】入门基础介绍(下)输入输出,函数重载,缺省与引用

文章目录 7. C输入与输出8. 缺省参数9. 函数重载10. 引用10. 1 引用的概念10. 2 引用的特性10. 3 引用的使用10. 4 const引用10. 5 指针和引用的关系 11. inline12. nullptr 7. C输入与输出 iostream是 Input Output Stream 的缩写&#xff0c;是标准输入、输出流库&#xff0…

k8s 中存储之 PV 持久卷 与 PVC 持久卷申请

目录 1 PV 与 PVC 介绍 1.1 PersistentVolume&#xff08;持久卷&#xff0c;简称PV&#xff09; 1.2 PersistentVolumeClaim&#xff08;持久卷声明&#xff0c;简称PVC&#xff09; 1.3 使用了PV和PVC之后&#xff0c;工作可以得到进一步的细分&#xff1a; 2 持久卷实验配置…

什么是安全运营中心 SOC?

SOC 代表安全运营中心&#xff0c;它是任何企业中负责组织安全、保护企业免受网络风险的单一、集中的团队或职能。 安全运营中心将管理和控制业务运营的所有安全要素&#xff0c;从监控资产到雇用合适的人员和流程&#xff0c;再到检测和应对威胁。 在本文中&#xff0c;我们…

sqli-labs less-14post报错注入updatexml

post提交报错注入 闭合方式及注入点 利用hackbar进行注入&#xff0c;构造post语句 unameaaa"passwdbbb&SubmitSubmit 页面报错&#xff0c;根据分析&#xff0c;闭合方式". 确定列数 构造 unameaaa" or 11 # &passwdbbb&SubmitSubmit 确定存在注…

【Blender Python】7.一些运算、三角函数以及随机

概述 要用Blender进行程序生成&#xff0c;数学计算是少不了的&#xff0c;Python支持一些常规的表达式计算&#xff0c;而另外一些相关的数学函数则在math模块中。 一些基础的运算 取余、除法、整除 >>> 21 % 4 1>>> 21 / 4 5.25>>> 21 // 4 5…

视频画面提取保存为图片:简易方法与实用工具

如果需要在视频里随机截取某一帧作为照片来保存或分享&#xff0c;如何快速剪辑多个视频&#xff1f;幸运的是&#xff0c;如今有多种简易的方法和实用的工具可以帮助我们轻松实现这一目标。 1打开“媒体梦工厂”用到“视频封面”功能&#xff0c; 2在此功能里切换到“抽帧/提取…

通过实时可视性转变云安全

Upwind首席执行官 Amiram Shachar 讨论了混合和多云环境中云安全的复杂性。 他概述了深入了解配置和实时洞察的必要性&#xff0c;以实现敏捷性和安全性之间的平衡。 还分享了解决错误配置和确保合规性的策略&#xff0c;建议在云部署中采取主动的风险管理方法。 随着混合云…

毕设 大数据电影数据分析与可视化系统(源码+论文)

文章目录 0 前言1 项目运行效果2 设计概要3 最后 0 前言 &#x1f525;这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师…

Bluetooth Channel Sounding中关于CS Event Subevent的详细介绍

BLE CS Event & Subevent定义&#xff1a; BLE CS Event事件定义为&#xff1a;CS 事件被定义为从同一个 LE piconet 物理通道连接事件锚点具有相同偏移Offset的所有 CS Subevent子事件组&#xff0c;如图 1所示&#xff0c;具体参考Vol 1, Part A 3.3.2.5.2 Characteristi…

自动驾驶系统研发系列—如何选择适合自动驾驶的激光雷达?从基础到高端全解读

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

CSS圆角

在制作网页的过程中&#xff0c;有时我们可能需要实现圆角的效果&#xff0c;以前的做法是通过切图&#xff08;将设计稿切成便于制作成页面的图片&#xff09;&#xff0c;使用多个背景图像来实现圆角。在 CSS3 出现之后就不需要这么麻烦了&#xff0c;CSS3 中提供了一系列属性…