编译和连接

news2024/11/8 4:38:45

前言:哈喽小伙伴们,从我们开始学习C语言到实现如今的成果,可以说我们对C语言的掌握已经算是精通级别了,但是我们只学习了怎么写代码,却没怎么了解过代码的背后是怎么工作的。

那么今天这篇文章我们一起来学习C语言的最后一部分知识——编译和连接

跟上节奏不要掉队哦!


一.翻译环境和执行环境

我们每次在写代码前,都要先创建一个形如test.c的源文件,这个文件是一个文本信息文件,在它里边我们只能看到我们所写的代码,比如说博主我写的第一个C语言代码:

那么我们如何才能实现代码的功能呢???

这就要通过翻译环境来实现了,也就是我们常用的各种编译器这些编译器又叫做集成开发环境,它们能够对代码进行翻译生成一个可执行的程序(后缀.exe)

 

最后可执行程序再通过执行环境来实现代码的运行,执行环境就是我们电脑的操作系统等

 下面我们来详解这两个环境。


1.翻译环境

翻译环境又分为两个部分:编译和链接

那么对于博主所使用的VS2019编译器来说,它们分别代表着cl.exe和link.exe两个可执行程序。

  • 组成一个程序的每个源文件通过编译过程分别转换成目标文件(后缀.obj)
  • 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
  • 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,同时它也可以搜索程序员所创建的个人程序库,引入其中所用到的函数。

那么对于编译而言,它又分成三个阶段来完成:

  1. 预编译(预处理)
  2. 编译
  3. 汇编

预处理阶段包括:

  • 注释被替换成一个空格(删除)
  • 头文件的包含 #include<>
  • 预处理指令的执行 #define的替换

编译过程就是将C语言代码翻译成汇编代码,包括:

  1. 词法分析
  2. 语法分析
  3. 语义分析
  4. 符号汇总

那么汇编的作用就是:

将汇编代码翻译成二进制的指令,并生成后缀为.obj的目标文件。

对于链接来说,主要作用是:

链接目标文件和链接库生成可执行的二进制程序

 大家只需要记住这些步骤以及每一步要做的事即可,不必深究。

接下来我们来看运行环境。


2. 运行环境

运行环境也可以说就是程序的执行过程:

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

这些过程不做展开讲解,小伙伴们也只需要了解即可。


二.预处理

1.预定义符号

__FILE__          进行编译的源文件

__LINE__          文件的当前行号

__DATE__        文件被编译的日期

__TIME__         文件被编译的时间

__STDC__        如果编译器遵循ANSI C,其值为1,否则未定义

注意上边都是双杠。

这些预定义符号都是C语言所内置的,可以直接使用:

#include<stdio.h>
int main()
{
	printf("%s\n", __FILE__);
	printf("%d\n", __LINE__);
	printf("%s\n", __DATE__);
	printf("%s\n", __TIME__);
	return 0;
}

得到结果:

但是当我们使用__STDC__时,编译器却报错了:

 

这说明博主当前使用的VS2019没有定义该预处理指令。


2.#define

(1)定义标识符

#define我们前边已经了解过了,它是一个宏定义指令可以将任意类型的数据用一个新的名字代替,例如:

#define N 100

#define INT int

#define STR "abcdefg"

前者为新名字,后者为数据。

同时#define也可以重命名一个表达式,例如:

#define M 4+5

之后就可以把M当做4+5来用。

但是值得注意的是,M的值并不是4+5的结果9,而是4+5这个表达式

#include<stdio.h>
#define M 4+5
int main()
{
	int n = 5 * M;
	printf("%d", n);
	return 0;
}

来看这个代码,n的值会是多少呢,45吗???

结果却是25

实际的运算式为:

int n = 5 * 4 + 5; 

 如果想得到45,还得给M加个()


(2)定义宏

事实上#define还有类似于函数的用法:

它存在一个机制,允许把参数替换到文本中,这种实现就被称为宏。

怎么理解这句话呢???下面我们来看宏的声明

#define name(parament-list) stuff

parament-list是一个由逗号隔开的符号表,也就是我们所要使用的参数表

值得注意的是,参数列表的左括号必须和name紧连,如果之间有空格参数列表就也会被认为是stuff的一部分

还是不够理解?没问题,下面我们就通过具体例子来讲解到底怎么用:

#include<stdio.h>
#define ADD(x,y) x+y
int main()
{
	int a = 20;
	int b = 30;
	int c = Add(a, b);
	printf("%d", c);
	return 0;
}

来看,我们定义了ADD(x,y)这样一个宏,它的内容是x+y,也就是说当我们以后去调用这个宏时,他都会被替换成x+y。来看结果:

是不是感觉这个宏和函数非常的相似?事实确实如此。

但是宏也有一些弊端,比如说如果我将上边的运算式改为:

    int c = 4 * ADD(a, b);

c的值会是什么呢?        50吗??

并不是,上述式子的实际形式为:

    int c = 4 * a + b;

因为就算是宏,他也是要执行#define的规则它只是会替换表达式,并不是替换成表达式的运算结果

因此关于#define的应用,该加括号的时候一定要记得加


(3)宏和函数的对比

那么既然宏的功能与函数这么相似那么我们该如何在它们两个之间进行选择呢???

一般情况下,宏更适用于一些简单的运算,就比如我们上边所写的ADD加法宏

那么对于简单的运算,为什么宏就一定优于函数呢???

有以下两个原因:

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
  2. 更为重要的是,函数的参数必须声明为特定的类型。而宏参数不需要定义类型就可以使用。

 函数相较于宏,不仅仅要执行运算,还要执行函数的调用和返回,这些都需要花费时间,所以对于简单的运算,宏是优于函数的。

那什么时候该用函数呢???

对于一些相对复杂的功能,用宏可能写都写不出来的,函数就更占优势。

此外,宏是不方便调试的,如果我们的代码出错了,想要调试找错误,这时候就非常麻烦。

虽然宏参数不需要类型,但是在特定情况下我们是必须确定参数类型的,这时候就必须用函数。

所以说,宏和函数各有千秋,小伙伴们一定要对症下药。 


3.#undef

该预处理指令的作用是移除一个宏定义

#include<stdio.h>
#define X 1000
int main()
{
	int x = X;
	printf("%d\n", x);
#undef	X
	int X = 1;
	printf("%d\n", X);
	return 0;
}

先将X定义为1000,然后赋值给x并输出,随后我取消X的定义,又重新给X赋值,结果如下:


4.条件编译

何为条件编译???其实是针对一段代码的编译与否

有时候我们写代码时可能会出现这种情况:某些代码可能只需要在特定的情况下运行,也就是说这段代码时用时不用,这时候就产生一个问题:

不用时,它在那里占着空间,但是要是把它删了,用的时候还得再重新写,这就很麻烦,所以,我们引出条件编译:只有当满足条件时,这段代码才编译,否则这段代码将会是注释的效果

条件编译和if-else条件判断语句很相似,一样分为好几种写法,下面我们就来一一讲解。


 (1)单分支

单分支,也就是只有一次判断,其语法为:

#if 常量判断表达式

        //执行语句

#endif

下面我们来看一个实际例子:

 

 对于图1,因为m==5,所以能够执行输出语句,而图2不满足,所以输出语句变得暗淡,可以理解为被注释掉了


(2)多分支

多分支,也就是会进行多次判断,语法为:

#if 常量判断表达式

        //执行语句

#elif 常量判断表达式

        //执行语句

#else 

        //执行语句

#endif

这里的运算和if-else语句完全相同,博主就不在图示了,只点出重要的信息:

哪个常量判断表达式满足,就执行哪里的语句,都不满足,则执行else后的语句


三.结语

到此为止呢,关于C语言所有知识的大纲博主已经统统分享完啦

现在回想起来,一开始博主我还是非常抵触写文章的,但是慢慢写下来就会发现写文章也是一种乐趣,因为能够把自己对知识的理解写出来真的很有成就感。

OK,第一阶段到此结束,后续有空博主也还会分享过于C语言里边的一些细小琐碎的知识。

第二阶段,数据结构,敬请期待!!!

最后还是希望大家能给博主点点关注,一键三连!!!

我们下期再见啦!!!

 

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

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

相关文章

spark获取hadoop服务token

spark 作业一直卡在accepted 问题现象问题排查1.查看yarn app日志2.问题分析与原因 问题现象 通过yarn-cluster模式提交spark作业&#xff0c;客户端日志一直卡在submit app&#xff0c;没有运行 问题排查 1.查看yarn app日志 appid已生成&#xff0c;通过yarn查看app状态为…

Linux的权限管理操作

1、权限的基本概念 在多用户计算机系统的管理中&#xff0c;权限是指某个特定的用户具有特定的系统资源使用权利。 在Linux 中分别有读、写、执行权限&#xff1a; 权限针对文件权限针对目录读r表示可以查看文件内容&#xff1b;cat表示可以(ls)查看目录中存在的文件名称写w…

css钟表数字样式

如图&#xff1a; 代码 font-size: 28px;font-family: Yourname;font-weight: 500;color: #00e8ff;

YOLOv5/v7/v8改进实验(七)之使用timm更换YOLOv8模型主干网络Backbone篇

🚀🚀 前言 🚀🚀 timm 库实现了最新的几乎所有的具有影响力的视觉模型,它不仅提供了模型的权重,还提供了一个很棒的分布式训练和评估的代码框架,方便后人开发。更难能可贵的是它还在不断地更新迭代新的训练方法,新的视觉模型和优化代码。本章主要介绍如何使用timm替…

企业实施MES管理系统时,是否需要一物一码

随着科技的不断进步&#xff0c;企业对于生产管理和效率提升的需求日益增长。MES管理系统作为一种先进的生产管理工具&#xff0c;受到了越来越多企业的关注和应用。然而&#xff0c;在部署MES管理系统时&#xff0c;是否需要实施一物一码的条码进行管理&#xff0c;往往成为企…

“人间烟火”背后,长沙招商引资再出圈

连续多年&#xff0c;长沙荣膺全国最具幸福感城市。同时&#xff0c;长沙也被誉为“中部崛起的引擎城市”。长沙不仅有网红城市的人间烟火气&#xff0c;更以创新的精神&#xff0c;优质的营商环境&#xff0c;高效的政府服务&#xff0c;丰富的人才资源和深厚的产业基础&#…

徐宗本院士将在2023年CCF中国软件大会上作特邀报告

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;邀请徐宗本院士作大会特邀报告。 特邀嘉宾 徐宗本 中国科学院院士 数学家、中国科学院院士、西安交通大学教授。 主要从事应用数学、智能信息处理、机器学习基础理论研究。曾提出稀疏信息处理的L(1/2)正则化理…

uni-app:实现当前时间的获取,并且根据当前时间判断所在时间段为早上,下午还是晚上

效果图 核心代码 获取当前时间 toString()方法将数字转换为字符串 padStart(2, 0)&#xff1a;padStart()方法用于在字符串头部填充指定的字符&#xff0c;使其达到指定的长度。该方法接受两个参数&#xff1a;第一个参数为期望得到的字符串长度&#xff0c;第二个参数为要填充…

【分布式】入门级NCCL多机并行实践 - 02

# 背景知识 大模型和分布式训练对数据的吞吐量以及并行度都有很高的要求&#xff0c;NCCL就是在这个背景下诞生的。 如果你是一个只会写写Python&#xff0c;调用PyTorch和Horovod的算法萌新&#xff0c;可能对于分布式底层的东西不太了解&#xff0c;在下岗热潮中被主管逼着…

分布式锁之mysql 锁

文章目录 使用数据锁&#xff1a;悲观锁 或者 乐观锁悲观锁乐观锁mysql锁总结 使用数据锁&#xff1a;悲观锁 或者 乐观锁 一个sql&#xff1a;直接更新时判断&#xff0c;在更新中判断库存是否大于0 update table set surplus (surplus - buyQuantity) where id 1 and (surp…

Occupancy占据网络论文讲解与分析

一.MonoScene 1.概要 a.使用单目相机&#xff0c;不用深度估计和点云来实现占据网络。 b.提出了一种2D-3D的一种转换方法。 c.在3D-unet底部加入3DCRP来捕获长距离的一个信息。 2.模型结构 图像先经过一个2D的unet结构&#xff0c;这里论文里用的预训练的EfficientNet&am…

I350网卡烧录oprom,通过UEFI PXE引导方案

Intel发布的I350标准固件都是用于LOM设计的。固件已配置LOM模式&#xff0c;不需要搭配外挂flash&#xff0c;将efi driver包进BIOS中就可以使用PXE功能&#xff0c;因此NIC类型的时候直接烧录oprom会报错。 如使用外部flash存放PXE ROM&#xff0c;需要将固件修改为NIC的配置…

蓝桥杯(砝码称重,C++)

思路&#xff1a; 1、用到动态规划思想。 2、用ans[i][j]记录用前i个砝码&#xff0c;能不能称出重量j。 3、详细思路见代码注释&#xff0c;易懂。 #include<iostream> #include<cmath> using namespace std; int main() {int n;int a[110];//记录每个砝码重量int…

8+非肿瘤+线粒体+实验生信思路解析

今天给同学们分享一篇非肿瘤线粒体实验的生信文章“Role of mitochondrial metabolic disorder and immune infiltration in diabetic cardiomyopathy: new insights from bioinformatics analysis”&#xff0c;这篇文章于2023年2月1日发表在J Transl Med期刊上&#xff0c;影…

EPLAN_004#常用功能(四)

线号&#xff1a;火线L&#xff0c;零线N&#xff0c;正极P&#xff0c;负极是M 一、基于设备的设计 也可以通过下面的&#xff08;设备选择&#xff09;进行选择 如果是批量选型&#xff0c;可以在设备导航器中进行多个相同元器件进行选型。&#xff08;筛选器可以自定义&…

前端使用qrcodejs2插件实现根据网址生成二维码

实现效果&#xff1a; 实现方法&#xff1a; 1.安装插件 npm install --save qrcodejs2 2.可以全局引入&#xff0c;也可以只在使用的vue文件中引入 import QRCode from qrcodejs2; 3.在vue文件的template中设置放置二维码的div <div id"qrcode"></di…

8个视频剪辑素材网站,免费下载

找视频剪辑素材就上这8个网站&#xff0c;免费下载&#xff0c;可商用&#xff0c;赶紧收藏起来~ 免费视频素材 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYxMjky 菜鸟图库网素材非常丰富&#xff0c;网站主要还是以设计类素材为主&#xff0c;高清视频素材也很多…

ssm+vue的毕业生跟踪调查反馈管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的毕业生跟踪调查反馈管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层…

整理uvc驱动相关函数的调用流程

目录 1、uvc_video.c初始化函数的调用关系 2、uvc_queue.c3、uvc_v4l2.c4、v4l2-core5、数据传输1、分配一个gadget请求2、请求一个queue 1、uvc_video.c // uvc_video.c uvc_video_encode_header uvc_video_encode_data uvc_video_encode_bulk uvc_video_encode_isoc uvcg_vi…

Python学习基础笔记七十八——Socket编程1

现在的软件开发基本上都需要网络通讯。 不管是传统计算机软件&#xff0c;还是手机软件&#xff0c;还是物联网嵌入系统软件&#xff0c;这些都要和其他网络系统进行通讯。 而当今世界基本上都是使用TCP/IP协议进行通讯的。 TCP/IP协议是一种传输数据的方案。 收发信息的程序…