编译链接和预处理
🌺在本次的博客当中我们可以一起来学习一下C语言的编译链接以及预处理的知识。
🌺要谈到C语言的编译和链接就肯定得从我们C语言程序运行的时候所产生的诸多的文件类型了。我们正常情况下编辑书写代码的文件为 .c 文件,我们的 .c 文件通过遍历和链接步骤之后生成可以执行的 .exe 文件。我们的编译和链接步骤当中的编译步骤又可以细分为预编译,编译,汇编三个步骤。我们可以通过一张图片进行感受编译和链接的用法。
🌺在编译的步骤当中我们的.c文件首先会经过预处理生成一个.i文件,之后我们的.i文件又会经过编译生成我们的.s文件,最后我们的.s文件通过汇编操作最终生成一个.o文件。到此为止我们的编译的步骤才算到此为止。由于我们在编译的过程中生成的一系列文件不同,所以我们每个阶段所进行的操作也各不相同。首先在我们的预编译阶段主要会进行如下三个操作:1.#include 头文件的包含 2.#define 定义符号的替换和删除 3.注释的删除 在预编译阶段我们进行的都是文本的替换操作 并不会对代码进行过多的更改。
🌺下一个步骤叫做编译,在这个步骤当中编译器将C语言代码翻译成了汇编代码。在这一步骤我们的编译器会对代码进行语法分析,词法分析,语义分析,以及符号汇总。在我们的符号汇总阶段汇总的符号会形成一张符号表。最后一个阶段就是我们的汇编的阶段。我们通常情况下我们在上一阶段转换完成的汇编代码翻译成二进制的指令。并存放在我们的目标文件当中。
🌺当我们的编译阶段完成之后就来到了我们的链接阶段了。在这一阶段我们会进行的操作分为两步:1.合并段表 2.符号表的合并和重定位 当我们完成之后就会产生一个 .exe 文件。我们不同的文件之间的函数的使用会在这一阶段进行相互连接。
🌺在完成上述的步骤之后我们程序的编译链接的步骤也就完成了。接下来我们在来认识一下我们的预处理操作:我们在上面的讲解当中说到过,我们头文件的包含,宏定义,注释的删除等操作都是在这一步骤当中完成的。我们介绍预处理操作的时候主要针对于我们的宏定义进行的。
🌺我们最常见的宏定义是使用我们的 #define 进行定义的。假如我们在我们的程序当中定义了一个宏常量,那么我们在程序进行预处理的时候会将我们的关键字直接替换成我们对应的值。举一个简单的代码进行理解:
🌺就像是我们所执行的代码一样。通过内部分析我们可以知道我们的程序会在预处理的阶段直接将我们的NUM 替换成为我们定义的常量的数字12。除了使用宏定义定义一个常量之外我们呢还可以使用宏定义定义一个表达式,利用宏定义的替换操作达到函数的表达效果。在举一个简单的例子: 🌺看到这里相信你肯定会觉得宏定义很方便对吧。确实,宏定义使用起来确实很方便,但是我们使用宏定义的时候得格外小心。因为我们的宏定义不会进行语法的检查,只会进行无脑的替换,即使我们在宏定义当中的语法是错误的我们的程序也会直接替换到正式的代码当中,对编写代码造成困扰。还有就是我们对于复杂的宏定义的替换难以估计其输出的值。举一个简单的例子:
🌺按照我们正常的代码的编写的逻辑我们上面的代码应该可以正常的运行但是实际上却不是,这是因为我们在if当中使用了宏定义替换,而我们的宏定义替换已经定义了一个分号,而我们在if语句当中又重新加入了一个分号,使得我们下面的if无法进行特定的匹配。我们可以试着将我们宏定义当中的分号去掉,或者给我们if语句当中的语句加一个括号进行一下检验。 🌺上面仅仅是编译的错误,较多的使用宏定义可能还会造成无法预知的错误,比如下图的代码:
🌺按照我们正常的逻辑来分析代码:我们的ADD宏定义中应该得到的结果为8,之后乘3得到的结果应该是24。但是我们实际得到的结果却是18,这是因为我们的宏定义并不会主动给我们的定义内容加上括号。所以我们程序替换之后应进行的计算为:3+5*3 会计算3*5得到15 之后加上3。得到我们最终的答案18。要想解决上面的问题我们可以增加括号。更改之后的代码如下:
🌺 我们使用宏定义还可能会产生对后面的代码产生副作用。所谓的副作用指的是我们在使用 ++ 自增的语句的时候对本身变量产生的影响。
🌺看到上面的代码可能大家会很疑惑,为什么会由上面的结果呢?我们就来进行分析:首先我们调用宏定义的时候传入的参数为x++,y++。第一次使用的时候将我们的x更改为6,y更改为9,之后我们就会继续调用(b)的步骤,输出9。我们的y又会自增一变成10。所以就产生了上图所示的代码的运行结果。如果我们使用函数进行定义这一项功能的话就不需要考虑的这么复杂。 🌺我们的自增操作只会在函数的调用的时候进行一次,并不会在函数当中带入a++和b++而是带入一个值,这样就是我们代码的运行逻辑更加清晰。你可能会想:那什么时候使用宏定义什么时候使用函数呢?
🌺当我们的语句比较短的时候我们就可以使用我们的宏定义进行操作。因为我们的宏定义代码量比较短,我们使用函数的话,还会进行函数栈帧的创建。在我们创建函数所花费的时间要远远长于我们代码运行计算的时间。所以对于我们逻辑比较清晰,代码量比较短的代码来说我们使用宏定义程序运行的速度要快于我们的函数。我们可以通过这一特点进行宏定义和函数的选择。
🌺接下来我们再来认识一下我们的条件编译的内容。当我们在编写程序的时候在前面定义了一个宏定义之后我们之后的所有的操作都会自动进行宏替换,但是假如我们在使用完之后不想再让这个宏定义发挥功能了应该怎么办呢?这时候我们可以使用 #undef 命令进行宏定义的取消。沃尔玛呢仅需要将我们之前定义好的宏常量在我们的undef当中重新定义一遍即可。执行代码如下:
🌺 对于我们最常见的条件编译主要为 #if...#elif...#else...#endif或者 #if defined。在这些条件编译当中我们可以进行代码的选择性编译,针对我们的宏定义是否进行定义而控制我们的编译的内容。举一个简单的例子来说:
🌺我们所要在选择编译当中所要进行的操作结构如上图(上图中的为便于理解的伪代码,我们可以根据伪代码进行实际代码的完善)我们实际的代码的编写如下:
🌺但我们在前面定义了一个宏常量的话就可以对于这个宏常量进行判断,并输出特定的代码。其结构和我们的条件分支语句的思想大致相同。同样的我们也可以使用我们的 #if defined进行判断。我们的 #if defiend 的作用同样是对于我们之前定义的变量进行判断。如果定义了的话就执行相应的指令,没有的话就直接跳过。所示的代码如下:
🌺就像是我们上面所展示的示例效果那样。
🌺那么此上就是本次博客的全部内容了,感谢您的观看。
相关文章:

编译链接和预处理
🌺在本次的博客当中我们可以一起来学习一下C语言的编译链接以及预处理的知识。 🌺要谈到C语言的编译和链接就肯定得从我们C语言程序运行的时候所产生的诸多的文件类型了。我们正常情况下编辑书写代码的文件为 .c 文件,我们的 .c 文件通过遍历…...

基于sprinmgboot实现实习管理系统的设计【源码+论文】
基于sprinmgboot实习管理系统的设计与实现演示摘要 随着信息化时代的到来,管理系统都趋向于智能化、系统化,实习管理也不例外,但目前国内仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大,人工管…...

动态规划入门
前言:首先,什么是动态规划? 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思…...

day26 SpringBootWeb案例(二)
目录 SpringBootWeb案例 1. 新增员工 1.1 需求 1.2 开发 1.3 测试 2. 文件上传 2.1 简介 2.2 本地存储 2.3 阿里云OSS 3. 配置文件 3.1 Value 3.2 yml配置 3.3 ConfigurationProperties 4. 修改员工 4.1 需求 4.2 查询回显 4.3 保存修改 SpringBootWeb案例 前…...

力扣-《剑指offer》-哈希表-刷题笔记
目录 第一题:03.数组中重复的数字 第二题:39.数组中出现次数超过一半的数字 第三题:50.第一个只出现一次的字符 第四题:53. (0-n-1)中缺失的数字 第一题:03.数组中重复的数字 我的答案&…...

【SpringBoot】| 邮箱发送验证码,你会了吗?
目录🦁 题外话🦁 提前准备2.1 配置邮箱第三方登录2.1.1 点击设置——账户2.1.2 开启POP3/SMTP服务2.2 添加依赖2.3 yaml配置🦁 进入主题🦁 测试使用🦁 尾声3.1 安利一个生成验证码的工具类3.1.1 添加依赖3.1.2 编写配置…...

Linux系统安装部署及配置Grafana
TOC 用于 UI 展示 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpm1 安装 grafana 1.1 下载安装 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpmsudo yum install grafana-8.0.3-1.x86_64.rpm1.2 启动&状态查看 sudo syst…...
Python3 入门教程||Python3 输入和输出||Python3 File 方法
Python3 输入和输出 在前面几个章节中,我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python 两种输出值的方式: 表达式语句和 print() 函数。(第三种方式是使用文件对象的 write() 方法; 标准输出文件可以…...
有效的字母异位词(力扣刷题)
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2: 输入: s "rat", t "car" 输出: false 说明: 你可以假设字符串只包含小写字母。 …...

73、介绍下 HashMap 的底层数据结构
73、介绍下 HashMap 的底层数据结构 我们现在用的都是 JDK 1.8,底层是由“数组链表红黑树”组成,如下图,而在 JDK 1.8 之前是由“数组链表”组成。 1.Hash Hash叫做”散列表“,就是把任意长度的输入,通过散列算法&am…...

系统集成路由器OSPF动态、综合路由配置
实验任务:动态路由协议RIP、OSPF协议的内容和特点动态路由RIP、OSPF实验,建立拓扑pc1>>R1>>R2>>R3>>pc2,使pc1与pc2能相互通信,并配置PC端静默接口。熟悉配置vlan间路由技术:多层交换机虚拟接…...

【力扣周赛 338】
6354. K 件物品的最大和 - 力扣(Leetcode)袋子中装有一些物品,每个物品上都标记着数字 1、0或 -1。给你四个非负整数 numOnes、numZeros、numNegOnes和 k。袋子最初包含:numOnes 件标记为 1 的物品。numZeroes 件标记为 0 的物品。…...

大数据Flink进阶(八):Apache Flink架构介绍
Apache Flink架构介绍 一、Flink组件栈 在Flink的整个软件架构体系中,同样遵循这分层的架构设计理念,在降低系统耦合度的同时,也为上层用户构建Flink应用提供了丰富且友好的接口。...

Mars3d项目启动上的一些坑
前言 最近新入职了一家公司,公司新开了有个未来城市的项目,需要用到3D城市建模,公司老总选了Mars3d作为前端框架,项目分给我了,又是一个全新的领域,开搞吧! 下面是自己遇到的几个小问题&#x…...

通俗易懂【Springboot】 单文件下载和批量下载(多个文件合成一个压缩包下载)
文章目录一.单文件下载1.简单理解文件下载2.单文件下载的具体代码实现3.测试4.单文件下载整体代码二.多文件批量下载(多个文件合成一个压缩包下载)1.多文件下载的实现方式,这里使用了ZipOutputStream2.具体代码实现3.测试4.文件批量下载&…...

CnOpenData中国行政区划shp数据
一、数据简介 中国行政区划数据是重要的基础地理信息数据,目前不同来源的全国行政区划数据非常多,但能够开放获取的高质量行政区域数据少之又少。基于此,锐多宝的地理空间制作一套2013-2023年可开放获取的高质量行政区划数据。该套数据以2022…...

GPT-4零失误通关大厂模拟面试,offer拿到手软?与AGI首次接触
来源: FoxyearMeta “GPT-4可被视作AGI (通用人工智能)的早期版本。” 如若从他人口中说出,或许是无稽之谈—— 但是由微软雷蒙德研究院机器学习理论组负责人万引大神Sbastien Bubeck与2023新视野数学奖得主Ronen Eldan、2023新晋斯隆研究奖得…...

Hardhat 环境搭建及教程示例
一.安装node.js curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash nvm install 18 nvm use 18 nvm alias default 18 npm install npm --global # Upgrade npm to the latest version 二. 安装hardhat 2.1 创建hardhat安装目录 mkdir hard…...

复杂链表的复制-剑指Offer35-java
一、题目描述 请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。 示例 1: 输入:head [[7,null],[13,…...

【Linux】进程理解与学习Ⅰ-进程概念
环境:centos7.6,腾讯云服务器Linux文章都放在了专栏:【Linux】欢迎支持订阅🌹相关文章推荐:【Linux】冯.诺依曼体系结构与操作系统进程概念什么是进程?进程是什么?我们打开任务管理器可以看到有…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...

android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...