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

编译和连接

前言:哈喽小伙伴们,从我们开始学习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	Xint 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语言里边的一些细小琐碎的知识。

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

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

我们下期再见啦!!!

 

相关文章:

编译和连接

前言&#xff1a;哈喽小伙伴们&#xff0c;从我们开始学习C语言到实现如今的成果&#xff0c;可以说我们对C语言的掌握已经算是精通级别了&#xff0c;但是我们只学习了怎么写代码&#xff0c;却没怎么了解过代码的背后是怎么工作的。 那么今天这篇文章我们一起来学习C语言的最…...

常见分布整理

概率论 - 常见分布&#xff08;及其分布表&#xff09; 常见分布的期望和方差 离散型分布 两点分布 有2种结果&#xff0c;实验只做1次 X~b(1,p)则有 P(X k) pk (1-p)1-k,k 0, 1 数学期望&#xff1a;E(X) p 方差&#xff1a;D(X)p(1-p) 二项分布 P(A) p&#xff0…...

ubuntu终端命令行下如何使用NetworkManager(netplan)来配置wifi网络

最近在给家里折腾一个文件共享服务器给家里的小米摄像头保存监控视频用。树莓派太贵了&#xff0c;找来找去发现香橙派orangepi zero3 是最低成本的替代解决方案&#xff08;网络足够快&#xff0c;CPU的IO能力足够强&#xff09;&#xff0c;香橙派orangepi zero3的操作系统是…...

GO学习之 goroutine的调度原理

GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 8、GO学习之 函数(Function) 9、GO学习之 接口(Interface) 10、GO学习之 网络通信(Net/Htt…...

CUDA学习笔记5——CUDA程序错误检测

CUDA程序错误检测 所有CUDA的API函数都有一个类型为cudaError_t的返回值&#xff0c;代表了一种错误信息&#xff1b;只有返回cudaSuccess时&#xff0c;才是成功调用。 cudaGetLastError()用来检测核函数的执行是否出错cudaGetErrorString()输出错误信息 #include <stdi…...

虹科 | 解决方案 | 机械免拆压力测试方案

对于发动机的气门卡滞或气门开闭时刻错误、活塞环磨损、喷油嘴泄漏/堵塞等故障&#xff0c;往往需要解体发动机或拆卸部件才能发现&#xff1b;而对于某些轻微的故障&#xff0c;即使解体了发动机后也经常难于肉眼判别 虹科Pico提供的WPS500压力测试方案&#xff0c;可以动态测…...

Python数据挖掘实用案例——自动售货机销售数据分析与应用

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前学习C/C、算法、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&…...

深度学习技巧应用29-软件设计模式与神经网络巧妙结合,如何快速记忆软件设计模式

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下软件设计模式与神经网络巧妙结合&#xff0c;如何快速记忆软件设计模式。我们知道软件设计模式有23种&#xff0c;考试的时候经常会考到&#xff0c;但是这么种里面我们如何取判断它呢&#xff0c;如何去记忆它呢&a…...

中文编程开发语言工具应用案例:ps5体验馆计时收费管理系统软件

中文编程开发语言工具应用案例&#xff1a;ps5体验馆计时收费管理系统软件 软件部分功能&#xff1a; 1、计时计费功能&#xff1a;只需点开始计时即可&#xff0c;时间直观显示 2、商品管理功能&#xff1a;可以管理饮料等商品 3、会员管理功能&#xff1a;支持只用手机号作…...

绘制核密度估计图

简介 核密度估计图&#xff08;Kernel Density Estimation&#xff0c;KDE&#xff09;是一种用于估计数据分布的非参数方法&#xff0c;通常用于可视化和理解数据的分布情况。它通过平滑地估计数据的概率密度函数&#xff08;PDF&#xff09;来显示数据的分布特征&#xff0c…...

基于深度学习网络的蔬菜水果种类识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1数据集准备 4.2构建深度学习模型 4.3模型训练 4.4模型评估 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 clc; clear; close all; wa…...

UE4 距离场

在项目设置的渲染模块可打开距离场 把该节点连上&#xff0c;该节点的意思是&#xff0c;距离表面越近&#xff0c;材质显示值为0 不接近表面时&#xff1a; 接近表面时 可勾选该值即可看到距离场具体效果&#xff1a; 未接触表面时&#xff1a; 接触表面时&#xff1a; 产生…...

【SA8295P 源码分析 (四)】26 - QNX Ethernet MAC 驱动 之 emac_rx_thread_handler 数据接收线程 源码分析

【SA8295P 源码分析】26 - QNX Ethernet MAC 驱动 之 emac_rx_thread_handler 数据接收线程 源码分析 一、emac_rx_thread_handler():通过POLL 轮询方式获取数据二、emac_rx_poll_mq():调用 pdata->clean_rx() 来处理消息三、emac_configure_rx_fun_ptr():配置 pdata->…...

VR全景广告:让消费者体验沉浸式交互,让营销更有趣

好的产品都是需要广告宣传的&#xff0c;随着科技的不断发展&#xff0c;市面上的广告也和多年前的传统广告不同&#xff0c;通过VR技术&#xff0c;可以让广告的观赏性以及科技感更加强烈&#xff0c;并且相比于视频广告&#xff0c;成本也更低。 在广告营销中&#xff0c;关键…...

论文阅读 | RAFT: Recurrent All-Pairs Field Transforms for Optical Flow

RAFT: Recurrent All-Pairs Field Transforms for Optical Flow ECCV2020光流任务best paper 论文地址&#xff1a;【here】 代码地址&#xff1a;【here】 介绍 光流是对两张相邻图像中的逐像素运动的一种估计。目前碰到的一些困难包括&#xff1a;物体的快速运动&#xff…...

神经网络的发展历史

神经网络的发展历史可以追溯到上世纪的数学理论和生物学研究。以下是神经网络发展史的详细概述&#xff1a; 早期的神经元模型&#xff1a; 1943年&#xff0c;Warren McCulloch和Walter Pitts提出了一种神经元模型&#xff0c;被称为MCP神经元模型&#xff0c;它模拟了生物神经…...

【单元测试】--单元测试最佳实践

一、单元测试代码风格 编写单元测试代码时&#xff0c;遵循一致的风格和最佳实践是非常重要的&#xff0c;因为它有助于提高代码的可读性、可维护性和可靠性。以下是一些常见的单元测试代码风格和最佳实践&#xff1a; 命名约定&#xff1a; 测试方法的名称应当清晰、描述性&…...

llava1.5-部署

llava1.5 ——demo部署 下载代码和权重 新建weights文件夹&#xff0c;并下载到LLaVA/weights/中。->需要修改文件名为llava-版本&#xff0c;例如llava-v1.5-7b. 运行 启动控制台 python -m llava.serve.controller --host 0.0.0.0 --port 4006启动gradio python -m…...

倒计时 1 天|KCD 2023 杭州站

距离「KCD 2023 杭州站」开始只有 1 天啦 大家快点预约到现场哦&#xff5e; KCD 2023 活动介绍 HANGZHOU 关于 KCD Kubernetes Community Days&#xff08;KCD&#xff09;由云原生计算基金会&#xff08;CNCF&#xff09;发起&#xff0c;由全球各国当地的 CNCF 大使、CNCF 员…...

什么是模拟芯片,模拟芯片都有哪些测试指标?

模拟芯片又称处理模拟信号的集成电路 模拟集成电路主要是指由电容、电阻、晶体管等组成的模拟电路集成在一起用来处理模拟信号的集成电路。有许多的模拟集成电路&#xff0c;如运算放大器、模拟乘法器、锁相环、电源管理芯片等。 模拟集成电路的主要构成电路有&#xff1a;放…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...