【C语言】进阶——程序编译
目录
一:🔒程序环境
程序的翻译环境和执行环境
💡1.1翻译环境
预编译阶段:
编译阶段:
汇编阶段:
链接阶段:
💡1.2运行环境
二:🔒预处理详解
💡2.1预处理符号
💡2.2#define
#define定义标识符
#define定义宏
#define替换规则
🔒#和##
💡#的作用
💡##的作用
带副作用的宏参数
三:宏与函数的对比
💡命名约定
四:🔒条件编译
五:🔒文件包含
💡头文件的包含方式
💡避免头文件被重复引用
一:🔒程序环境
程序的翻译环境和执行环境
在ANSIC任何一种实现中,存在两种不同的环境
1.翻译环境,在这个环境下源代码被转换成可执行的机器指令
2.执行环境,用于实际代码运行
💡1.1翻译环境
1.组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
2.每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
预编译阶段:
1.头文件包含
#include 预处理指令
2.define定义的符号替换
#define 预处理指令
3.注释删除
以上这些都是文本操作
编译阶段:
把c语言代码翻译成了汇编代码
1、语法分析
2、词法分析
3、语义分析
4、符号汇总
汇编阶段:
把汇编指令翻译成了二进制的指令
形成符号表,这样就能够找到源文件外部的符号(只能汇总全局符号)
链接阶段:
1、合并段表
2、符号表的合并和重定位
💡1.2运行环境
程序执行的过程:
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序 的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。
二:🔒预处理详解
💡2.1预处理符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义符号都是语言内置的。
int main()
{printf("file:%s line:%d\n", __FILE__, __LINE__);return 0;
}
💡2.2#define
#define定义标识符
#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
注:在宏定义时,最好不要加分号 ( ; )
因为宏定义标识符,并不会进行计算,在编译阶段进行的是内容替换
#define MAX 100;
int main()
{int max = 0;if (1)max = MAX; //errorelsemax = 0;return 0;
}
这里宏定义会直接替换,将 100; 替换到 MAX位置
if (1)max = 100;;elsemax = 0;
#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
#define name(parament-list) stuff
//name表示名字
//parament-list 是以逗号隔开的参数
//宏的具体内容例子
//#define max(a,b) a+b
注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被认为是要替换的部分,参数列表就会被解释为stuff的一部分
同样也要注意因为宏是直接进行文本替换,然后才在程序中发生计算,所以如果不按照标准规定写宏,可能会产生bug
#define SQUARE(x) x*xint c=5SQUARE(5+1);//我们预期这里的内容是36,但是最终结果是11,是因为实际计算的是//5+1*5+1==11
所以在写的时候我们应该尽可能带上括号,防止因为优先级的问题出现bug
#define SQUARE(x) ((x)*(x))
#define替换规则
在程序中扩展 #define 定义符号和 宏 时,需要涉及几个步骤。
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。.
注意:
宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
🔒#和##
💡#的作用
如何把参数插入到字符串中?
int main()
{printf("who say!!!\n");printf("who" " say!!!\n");return 0;
}
字符串是具备自动连接的特点。
而define定义的符号,在"X" 里面不会被识别,我们可以用 #X 解决此问题
#define PRINT(n, format) printf("the value of "#n" is "format"\n", n)
int main()
{int a = 10;PRINT(a, "%d");//printf("the value of ""a"" is ""%d""\n", a);//printf("the value of a is %d\n", a);int b = 20;PRINT(b, "%d");return 0;
}
💡##的作用
##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。
#define CAT(v, n) v##n
int main()
{int value10 = 100;printf("%d\n", CAT(value, 10));printf("%d\n", value10);return 0;
}
注:
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。
带副作用的宏参数
简单来讲就是宏在执行的过程中,参数自身的值会发生变化,这个就叫做带副作用的宏的参数
#define MAX(a,b) ((a)>(b)?(a):(b))int main()
{int x=5,y=8;int c=MAX(x++,y++);printf("%d ",c);return 0;
}
//这里输出的是多少,9嘛?
//首先带入 ((5++)>(8++)?(a++):(b++)) 首先是进行ab大小的比较,在这里比较之后,
//a跟b跟别变成了6和9,然后执行后面的b++,最终的结果应该a=6 c=9 b=10
要避免写出这样的代码,宏是无法调试的
三:宏与函数的对比
宏通常被应用于执行简单的运算。
比如在两个数中找出较大的一个。
宏定义和函数的比较
属性 | #define宏定义 | 函数 |
---|---|---|
代码长度 | 每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方;每 次使用这个函数时,都调用那个、地方的同一份代码 |
执行速度 | 相对更快(简单的程序) | 存在函数的调用和返回的额外开 销,所以相对慢一些 |
操作符 优先级 | 宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括 号。 | 函数参数只在函数调用的时候求 值一次,它的结果值传递给函 数。表达式的求值结果更容易预 测。 |
带 有 副 作 用 的 参 数 | 惨数可能被替换到宏体中的多个位置,所以带有副作 用的参数求值可能会产生不可预料的结果。 | 函数参数只在传参的时候求值一 次,结果更容易控制。 |
参数类型 | 宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型。 | 函数的参数是与类型有关的,如 果参数的类型不同,就需要不同 的函数,即使他们执行的任务是 不同的。 |
调试 | 无法调试 | 函数可以逐语句调试的 |
递归 | 无法递归 | 可以递归 |
1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
所以宏比函数在程序的规模和速度方面更胜一筹。(相对简单定义)
2.更为重要的是函数的参数必须声明为特定的类型。
所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以
用于来比较的类型。
宏是类型无关的。
💡命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
那我们平时的一个习惯是:
把宏名全部大写
函数名不要全部大写(一般单词首字母大写)
四:🔒条件编译
条件编译顾名思义就是满足条件才进行读取
1.
#if 常量表达式//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif
2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif
五:🔒文件包含
#include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方 一样。
这种替换的方式很简单:
预处理器先删除这条指令,并用包含文件的内容替换。
这样一个源文件被包含10次,那就实际被编译10次。
💡头文件的包含方式
本地文件
#include"test.h"
先从源文件所在目录进行查找头文件,然后再到标准函数库头文件所在目录下查找
#include<stdio.h>
直接从标准函数库头文件所在目录下查找
总的来说就是""的引用方式查找范围更广
但是!!!
本地文件还是按照 #include"test.h'' 的方式
库文件按照 #include<test.h>的方式
否则:查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
💡避免头文件被重复引用
防止多次头文件频繁引用
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
或者
#pragma once
以上便是我对【C语言】程序编译的介绍,文中不足之处,还望得到指点得以改善。感谢!!!
相关文章:

【C语言】进阶——程序编译
目录 一:🔒程序环境 程序的翻译环境和执行环境 💡1.1翻译环境 预编译阶段: 编译阶段: 汇编阶段: 链接阶段: 💡1.2运行环境 二:🔒预处理详解 &…...

记录阿里云服务器(Centos7.9)部署Thingsboard(3.4.2)遇到的一些问题
记录编译Thingsboard遇到的一些问题 部署了一个thingsboard项目到阿里云服务器上,历时十一天,遇到了很多困难,国内关于Thingsboard的资料确实很少,所以想着写一篇博客记录一下,或许能够给以后编译遇到类似问题的人一些…...
docker更新容器映射端口
一个容器已经暴露了一个端口被外界使用,但是这个端口被公司不允许使用,需要修改为其他的端口,怎么办? 1、删除原容器,重启新容器 删除已启动容器,从镜像重启新容器。2、修改原容器配置文件 3、生成镜像&…...
Pr快捷键
Pr快捷键 以下快捷键都是在英文输入法下 一、隐藏顶部项目信息 Ctrl\ 注意:是反斜杠,回车上面的按键二、单独放大窗口 选中面板按~键三、放大/缩小时间轴素材 \四、自动选中素材 序列菜单-选择跟随播放指示器五、快速定位间隙 SHIFT鼠标拖动素材 …...

94. 递归实现排列型枚举
题目: 94. 递归实现排列型枚举 - AcWing题库 思路: 1.全排列问题(坑位问题)---->递归搜索树---->用dfs深度优先搜索。 2. u表示当前坑位,state[u]表示坑位u存储的数据。因为不同坑位的数据不可以重复&#…...

白水三佳电脑ERP部署
安装宝塔面板,有这个方便很多,可以省下3天的环境部署时间。 移动端, 先取移动版的压缩包,上传至服务器/www/wwwroot/目录下面,直接解压到当前目录后会生成/www/wwwroot/m/的目录,移动版就在这里面了。以下…...

电流监测芯片SGM8199A2应用电路设计
SGM8199是一系列具有电压输出功能的双向电流监测芯片,用于监测共模电压范围内分流电阻上的压降,而不受电源电压的影响。该器件具有-0.1V至26V的宽共模电压范围输入。低偏移使得在监测电流时允许分流器上的满量程最大压降为10mV。SGM8199系列提供三种固定…...

第十五章 I/O输入输出
15,1输入输出流 流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种。I/O(Input/Output,(输出)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。虽然 I/O 流疆盘文件存取有关,但是程序的源和目的…...

进程(0)——计算机的中的软硬件【Linux】
进程(0)——计算机的中的软硬件【Linux】 一.硬件:1.1 冯诺依曼结构:1.2 存储金字塔1.2.1输入设备和存储器:1.2.2输出设备和存储器: 二.软件:2.1 操作系统2.1.1 如何理解管理: 博主自…...

Python中if not使用教程
大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 python中判断变量是否为None三种写法: 1、if x is None 2、if not x 3、if not x is None 理解成 if not (x is None) 结果是和1相反的 python中None、fals…...

Jmeter性能测试 —— jmeter之使用ServerAgent监控服务器
ServerAgent 性能测试时我们关注的重要指标是:并发用户数,TPS,请求成功率,响应时间,服务器的CPU,memory, I/O disk等。Jmeter的聚合报告可以查看并发数、吞吐量、请求成功率、响应时间等&#…...

C# Winform编程(7)文件处理技术
文件处理技术 System.IO命名空间System.IO命名空间常用的类System.IO命名空间常用的枚举 File类的常用方法File类的常用静态方法FileInfo类的常用方法File类和FileInfo类的区别文件夹类Directory的常用方法文件流类FileStream文件的读写读写二进制流读写内存流 System.IO命名空…...

前端工作方式要换了?HTMX简介:无需JavaScript的动态HTML
HTMX允许你使用扩展的HTML语法代替 JavaScript 来实现交互性。HTMX 在标记中直接为你提供HTTP 交互,并支持许多其他交互需求,无需求助于 JavaScript。这是一个有趣的想法,可能最终会影响到web前端的工作方式。让我们看看如何使用HTMX以及它的…...

动手学深度学习—使用块的网络VGG(代码详解)
目录 1. VGG块2. VGG网络3. 训练模型 1. VGG块 经典卷积神经网络的基本组成部分是下面的这个序列: 1.带填充以保持分辨率的卷积层; 2.非线性激活函数,如ReLU; 3.汇聚层,如最大汇聚层。 定义网络块,便于我…...

性能优化:JIT即时编译与AOT提前编译
优质博文:IT-BLOG-CN 一、简介 JIT与AOT的区别: 两种不同的编译方式,主要区别在于是否处于运行时进行编译。 JIT:Just-in-time动态(即时)编译,边运行边编译:在程序运行时,根据算法计算出热点代码…...

抖音同城榜:探索城市新潮流
随着科技的飞速发展,短视频已经成为了人们日常生活中不可或缺的一部分。作为短视频领域的佼佼者,抖音一直致力于为用户带来更丰富、更有趣的短视频内容。抖音同城榜应运而生,成为了最新、最热门的话题聚集地,吸引了大量潮流达人和…...

云表|低代码开发崛起:重新定义企业级应用开发
低代码开发这个概念在近年来越来越受到人们的关注,市场对于低代码的需求也日益增长。据Gartner预测,到2025年,75%的大型企业将使用至少四种低代码/无代码开发工具,用于IT应用开发和公民开发计划。 那么,为什…...
【算法题】2906. 构造乘积矩阵
题目: 给你一个下标从 0 开始、大小为 n * m 的二维整数矩阵 grid ,定义一个下标从 0 开始、大小为 n * m 的的二维矩阵 p。如果满足以下条件,则称 p 为 grid 的 乘积矩阵 : 对于每个元素 p[i][j] ,它的值等于除了 g…...

机器学习基础之《回归与聚类算法(4)—逻辑回归与二分类(分类算法)》
一、什么是逻辑回归 1、逻辑回归(Logistic Regression)是机器学习中的一种分类模型,逻辑回归是一种分类算法,虽然名字中带有回归,但是它与回归之间有一定的联系。由于算法的简单和高效,在实际中应用非常广…...

UWB安全数据通讯STS-加密、身份认证
DW3000系列才能支持UWB安全数据通讯,DW1000不支持 IEEE 802.15.4a没有数据通讯安全保护机制,IEEE 802.15.4z中指定的扩展得到增强(在PHY/RF级别):增添了一个重要特性“扰频时间戳序列(STS)”&a…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...

相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...