#define定义标识符详解
0.预定义符号
在讲解#define之前先给大家介绍几个预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C(标准C),其值为1,否则未定义
这些预定义符号都是编译器自带的,可以直接使用,我们可以用printf函数去打印。
#include "stdio.h"int main(){printf("%s\n",__FILE__);printf("%d\n",__LINE__);printf("%s\n",__DATE__);printf("%s\n",__TIME__);printf("%d\n",__STDC__);return 0;}
1. 语法分析
#define name stuff
name代表的是stuff新的名字
stuff代表的是实现的内容
name的名字最好要全大写!!!!!
就比如你总会用到一个数字,但是你还需要将它换值,如果我们不用#define来,我们就需要每次都写这个值,改值的时候也要一次次去修改,这给我们带来了很多的麻烦,所以这个时候我们可以使用#define来定义这个数值,给他取个名字,代表这个数值,改值的时候就可以直接在#define这里修改,改一次就行,大大提高了写代码的效率。
记住这一点,#define可以将name替换成stuff。一定是替换,将name换成stuff。
比如:
代码1
#include "stdio.h"int main(){int a = 10;int b = 20;int c = 30;if(a < 25)printf("aYES\n");if(b < 25)printf("bYES\n");if(c < 25)printf("cYES\n");return 0;}代码2
#define n 25int main(){int a = 10;int b = 20;int c = 30;if(a < n)printf("aYES\n");if(b < n)printf("bYES\n");if(c < n)printf("cYES\n");return 0;}
代码1如果修改25需要修改3次,但是代码2通过#define定义后,只需要修改n后面的值就行,这就大大提高了修改的效率。
1.1 举例分析
1.1.1 代码1
#define MAX 100int main(){int a = MAX;int a = 100;return 0;}
我们来看第一个,我们用#define定义了MAX的数值是100,所以在主函数使用的时候
int a = MAX;就相当于 int a = 100; 也就是把MAX替换成了100
1.1.2 代码2
#include "stdio.h"
#define sz sizeofint main(){int a = sz(int);int b = sizeof (int);printf("%d %d",a,b);return 0;}
第二个代码,是用#define来定义一个sz,代表sizeof关键字,所以在使用过程中,sz(int)就可以替换成sizeof(int)
1.1.3 代码3
代码1
#include "stdio.h"
#define do_forever for(;;)int main(){do_forever{printf("1");}return 0;}
代码2#include "stdio.h"int main(){for(;;){printf("1");}return 0;}
这两个代码是等效的,代码1我们用#define定义了一个死循环,当编译的时候do_forever就会被替换成for(;;)。
所以#define可以定义任何东西,但是在使用时候会被自动替换成定义后面的东西。
1.1.4代码4
代码1
#include "stdio.h"
#define PRINT printf("file:%s\nline:%d\ndate:%s\ntime:%s\n",__FILE__,__LINE__,__DATE__,__TIME__)int main(){PRINT;return 0;}
代码2
#include "stdio.h"
#define PRINT printf("file:%s\n\line:%d\n\date:%s\n\time:%s\n",__FILE__,__LINE__,__DATE__,__TIME__)int main(){PRINT;return 0;}
代码3
#include "stdio.h"int main(){printf("file:%s\nline:%d\ndate:%s\ntime:%s\n",__FILE__,__LINE__,__DATE__,__TIME__);return 0;}
首先看代码1,我们是#define定义了一个PRINT,它是后面的一大串打印内容的,大家是不是也会觉得很长。
所以我们优化看一下代码2,我们将其回车分成了4行,但是这里要记住,一定要在我们敲回车的地方➕一个‘ \ ’,告诉编译器这还是一个整体的内容,也就是将我们敲的回车给转义了。如果不加这个’ \ ‘,整体代码就会出问题。
1.2 #define末尾是否应该添加' ; '
答案是最好不要加分号,那又是为什么呢?
我们知道#define是替换,比如上面我们是将MAX直接替换成了100、sz直接替换成了sizeof、do_forever直接替换成了for(;;)等等。
那我们如果加上了分号' ; '之后呢?
代码1
#define MAX 100;int main(){int a = MAX;return 0;}
代码2
#define MAX 100;int main(){int a = 100;;return 0;}
代码3
#define MAX 100;int main(){int a = MAXreturn 0;}
首先代码1和代码2是等效的,我们可以看到加了分号之后,MAX是被替换成了100;,这里就有2个分号了,肯定是错误的。
那怎么去修改呢?
我们可以像代码3一样,在主函数那里也就是语句结尾,不加分号,等替换之后就有分号了,但是这跟咱们的书写习惯大大不同,所以最好不要加分号!
2. #define定义宏
2.1 语法分析
#define name( parament-list ) stuff
1.#define定义宏和函数很相似
2.name相当于函数名
3.parament-list 就相当于函数参数
4.stuff就是相当于这个函数的功能
注意⚠️⚠️:这里的左括号一定要跟name紧挨着
否则会name就会被替换成( parament-list ) stuff
比如:
代码1#define MAX(x,y) (x) > (y) ? (x) : (y)
int main()
{MAX(2,3);MAX(2,3);相当于:(2) > (3) ? (2) : (3);
}代码2#define MAX (x,y) (x) > (y) ? (x) : (y)
int main()
{MAX(2,3);MAX相当于:(x,y) (x) > (y) ? (x) : (y)
}
代码1是左括号紧挨着name的,那我们就可以用MAX(x,y)完成我们想要实现的功能。
代码2是没有紧挨着的,这时候会出现错误,因为此时是MAX相当于(x,y) (x) > (y) ? (x) : (y)
这时候MAX啥也不是,是个错误。
所以左括号必须跟name紧挨着!
总而言之还是要记住#define是替换的作用。
2.2代码举例
2.2.1 代码1
#define Square(x) x * x
我们定义了一个Square(x),它的作用是求一个数的平方,参数是x
Square(5);
所以我们在引用的时候,给括号里数字5
5 * 5
就相当于5*5,可以求出来这个数的平方。
但是如果是这样的代码,结果又会是多少呢?
#include "stdio.h"
#define Square(x) x * x
int main()
{int a = 5;printf("%d\n" ,Square( a + 1) );return 0;
}
正确答案是11
我相信很多人都会得出36,但是我们一直在说#define是替换,所以上面代码又可以变成如下
#include "stdio.h"
#define Square(x) x * x
int main()
{int a = 5;printf("%d\n" ,a + 1 * a + 1 );return 0;
}
对吧,这时候Square(a+1)会被替换成 a+1*a+1,优先乘法运算,结果是11
也就是说我们#define定义的,真的就是替换,你给什么替换什么,不会加括号,所以为了算出正确结果,一定要加括号,在#define宏定义时候,要多加括号。
正确代码:
#include "stdio.h"
#define Square(x) (x) * (x)
int main()
{int a = 5;printf("%d\n" ,Square( a + 1) );return 0;
}
2.2.2 代码2
#define DOUBLE(x) (x) + (x)
我们看这个#define定义的DOUBLE(x) 是求一个数的二倍,在这里我们给x加了括号避免了之前的错误,那下面代码输出的是什么呢?
int a = 5;
printf("%d\n" ,10 * DOUBLE(a));
我相信大多数人现在肯定是算出正确结果了,因为之前吃的亏
答案是55
和我们预想结果不一样呀,我们预想的是100,为什么不一样呢?
int a = 5;
printf("%d\n" ,10 * 5 + 5);
实际上,DOUBLE(a)是被替换成了5 + 5,但是前面有个10,还是乘法,就变成了10*5+5.那如何解决这个问题呢?还是加括号,代码如下
#define DOUBLE(x) ((x) + (x))
我们只需要整体加个括号就可以完美解决问题啦!
总结:
在#define定义宏的时候也就是类似函数,一定要多加括号,避免错误。
3.#define的替换规则
3.1 规则1
规则1 : 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。
如果是,它们首先被替换。
这是什么意思呢?我们拿代码举例:
#include "stdio.h"
#define n 100
#define MAX(x,y) ((x) > (y) ? (x) : (y))int main(){int a = 20;int c = MAX(a,n);printf("max是%d\n",c);return 0;}
在MAX(x,y)里,主函数传的参数里分别是a,n
a是我们定义的局部变量,而n是#define定义的符号,所以根据规则,要首先被替换。
就变成了这样:
int c = MAX(a,100);
n被替换成了100,之后再去替换#define定义的宏
int c = ((a) > (100) ? (a) : (100));
3.2 规则2
规则2:
再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
述处理过程。
这一点没什么说的,反正就是你在#define定义的时候一定要看先发生的是谁就行。
4.‘#’和‘##’的作用
4.1‘#’作用
我们先看这样一段代码
char* p = "hello ""bit\n";
printf("hello"" bit\n");
printf("%s", p);
此时输出的是
我们可以看到两个输出的都是一样的,所以我们发现字符串它是有自动连接的特点,也就是说我打印完“hello ”这个字符串,但是发现后面还有,那就接着打印。
我们明确了这个之后,思考这样一个问题 ,如何把参数插入到字符串中?
看这样一段代码:
#include "stdio.h"
#define PRINT(n) printf("n的值是%d\n", n)
int main()
{int a = 20;PRINT(a);return 0;
}
我们用PRINT想打印传入的参数的值,但是这么打印得到的结果是
首先第一点我们是要求a的值,我希望打印出来可以告诉我a的值是20,那这个时候‘#’就派上用场了。
#include "stdio.h"
#define PRINT(n) printf(#n"的值是%d\n", n)
int main()
{int a = 20;PRINT(a);return 0;
}
我们首先要知道printf函数是可以打印字符串的,字符串有自动连接的特点
我们想把a打印到屏幕上,就需要在n的前面加上‘#’这个符号,#n会被替换成"a"
#define PRINT(n) printf(#n"的值是%d\n", n)PRINT(a);
替换成
printf("a""的值是%d\n", a);
这样我们就打印出来想要的结果了
我们解决了名字的问题,但是如果输入个浮点数呢?别的类型呢?又该怎么办呢?
#include "stdio.h"
#define PRINT(n) printf(#n"的值是%d\n", n)
int main()
{int a = 20;PRINT(a);float b = 3.14;PRINT(b);return 0;
}
在这段代码里肯定是输出不来浮点数b的,因为%d被写死了,我们是否可以把打印的格式传参呢?
#include "stdio.h"
#define PRINT(n,format) printf(#n"的值是"format"\n", n)
int main()
{int a = 20;PRINT(a,"%d");float b = 3.14;PRINT(b,"%f");return 0;
}
我们是传入的字符串,所以在printf里可以直接写format,因为会被替换成
#define PRINT(n,format) printf(#n"的值是"format"\n", n)PRINT(a,"%d");
替换printf("a""的值是""%d""\n", n);PRINT(b,"%f");
替换printf("b""的值是""%f""\n", n);
这样就可以打印出来任何数据类型了。
4.2 ##的作用
##可以把位于它两边的符号合成一个符号,这个用途不大,但是很新奇。
比如:
#define Cat(x,y) x##yint main(){int helloworld = 100;printf("%d\n", Cat(hel,loworld));return 0;}
大家可以想一想输出的结果是多少?
我们一步一步来,先替换
Cat(hel,loworld)
替换成
hel##loworld
而##还可以连接左右的符号
hel##loworld
变成
helloworld
我们这个程序输出的就是helloworld的值,100.
相关文章:

#define定义标识符详解
0.预定义符号 在讲解#define之前先给大家介绍几个预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C(标准C)ÿ…...

开发者必备!如何将闲置iPad Pro打造为编程工具,使用VS Code编写代码
文章目录 前言1. 本地环境配置2. 内网穿透2.1 安装cpolar内网穿透(支持一键自动安装脚本)2.2 创建HTTP隧道 3. 测试远程访问4. 配置固定二级子域名4.1 保留二级子域名4.2 配置二级子域名 5. 测试使用固定二级子域名远程访问6. ipad pro通过软件远程vscode6.1 创建TCP隧道 7. ip…...

【Java 基础篇】Java 模块化详解
Java 9引入了一项重要的功能:模块化(Module System)。模块化是一种将代码和资源封装到可重用和独立的单元中的方法,它有助于改善代码的可维护性、可重用性和安全性。本文将介绍Java模块化的基本概念、如何创建和使用模块以及一些最…...
【2023面试题大全,都是常问面试题】
JAVA基础 面向对象和面向过程的区别 面向过程:基于步骤的编程方式,用函数把这些步骤一步一步地实现,然后在使用的时候一一调用则可 面向对象:基于对象的编程方式,通过定义类来描述对象的属性和行为,面向对…...

Bun 1.0 正式发布,爆火的前端运行时,速度遥遥领先!
文章目录 一、包子1.0二、Bun 是一个一体化工具包为什么包子存在 二、Bun 是一个 JavaScript 运行时Node.js 兼容性速度TypeScript 和 JSX 支持ESM 和 CommonJS 兼容性网络 API热重载插件 一、包子1.0 Bun 1.0终于来了。 Bun 是一个快速、一体化的工具包,用于运行…...
getchar函数设置为非阻塞
一. 前言 我们在学习C语言的时候,getchar都是阻塞的,等待用户输入字符并且输入回车后才返回。但是有时候我们希望把getchar设置为非阻塞,或者说,当我们遇到getchar函数变成非阻塞的了,我们应该怎么解决这个问题&#x…...
【超算作业调度系统--LSF】
集群服务器--LSF作业调度系统使用 0 Introdutction1 命令1.1 bsub--作业提交命令1.1.1 $ bqueues --查看现有队列信息;1.1.2 $lsload --查看各节点运行情况1.1.3 $bhosts --查看各节点空闲情况1.1.4 $busers --查看用户信息1.2 bsub --提交作业1.2.1 bsub OMP_NUM_T…...
L1-011 A-B分数 20
本题要求你计算A−B。不过麻烦的是,A和B都是字符串 —— 即从字符串A中把字符串B所包含的字符全删掉,剩下的字符组成的就是字符串A−B。 输入格式: 输入在2行中先后给出字符串A和B。两字符串的长度都不超过104,并且保证每个字符…...
PHPword解析内容支撑
因有些功能不支持,所以新增了某些功能,以防后期变动不好变更,手动做个记录 将公式替换成指定的符号,读取到 html 后读取 xml 解析公式,根据标记符号进行替换 文件名PhpOffice\PhpWord\Shared\XMLReader.php public fun…...

回归预测 | MATLAB实现RUN-XGBoost龙格库塔优化极限梯度提升树多输入回归预测
回归预测 | MATLAB实现RUN-XGBoost多输入回归预测 目录 回归预测 | MATLAB实现RUN-XGBoost多输入回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现RUN-XGBoost多输入回归预测(完整源码和数据) 1.龙格库塔优化XGBoost,…...

LLM-TAP随笔——语言模型训练数据【深度学习】【PyTorch】【LLM】
文章目录 3、语言模型训练数据3.1、词元切分3.2、词元分析算法 3、语言模型训练数据 数据质量对模型影响非常大。 典型数据处理:质量过滤、冗余去除、隐私消除、词元切分等。 训练数据的构建时间、噪音或有害信息情况、数据重复率等因素都对模型性能有较大影响。训…...
Linux- open() lseek()
文件描述符 文件描述符(File Descriptor,简称 FD)是 UNIX 和 UNIX-like 系统中用于代表和识别打开的文件或其他I/O资源的一种抽象标识。它是一个非负整数,内部由操作系统进行管理和分配。文件描述符可以代表文件、套接字、管道等…...
Halcon Tuple相关算子(一)
(1) tuple_length( : : Tuple : Length) 功能:返回输入元组中元素的个数。 控制输入参数: Tuple:输入元组; 控制输出参数:length:输入元组中元素的个数。 (2) tuple_find( : : Tuple, ToFind : Indices…...

基于图像形态学处理的路面裂缝检测算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ...................................................... %1:从文件夹中读取多个…...

PY32F003F18之窗口看门狗
一、PY32F003F18窗口看门狗特点: 即使窗口看门狗被禁止,窗口看门狗的"递减计数器"也会继续递减计数。 二、窗口看门狗复位的条件: 1、将"控制寄存器WWDG_CR"中的WDGA1,激活"窗口看门狗计数器等于0x3F"时,则产…...

SpingBoot:整合Mybatis-plus+Druid+mysql
SpingBoot:整合Mybatis-plusDruid 一、特别说明二、创建springboot新工程三、配置3.1 配置pom.xml文件3.2 配置数据源和durid连接池3.2.1 修改application.yml3.2.2 新增mybatis-config.xml 3.3 编写拦截器配置类 四、自动生成代码五、测试六、编写mapper.xml&#…...

计算机视觉与深度学习-经典网络解析-VGG-[北邮鲁鹏]
目录标题 VGG参考VGG网络贡献使用尺寸更小的$3 \times 3$卷积串联来获得更大的感受野放弃使用$11 \times 11$和$5 \times 5$这样的大尺寸卷积核深度更深、非线性更强,网络的参数也更少;去掉了AlexNet中的局部响应归一化层(LRN)层。 网络结构主要改进输入…...

入门级制作电子期刊的网站推荐
随着数字化时代的到来,越来越多的人开始尝试制作自己的电子期刊。如果你也是其中的一员,那么这篇文章可以帮助你制作电子期刊。无论是初学者还是有一定经验的制作者,都能快速完成高质量的电子期刊制作 小编经常使用的工具是-----FLBOOK在线制…...

软件测试内容整理
1. 软件测试 1.1. 定义 软件测试(英语:Software Testing),描述一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。换句话说,软件测试是一种实际输出与预期输出之间的审核或者比较过程。 软件测试的经典定…...

UniAccess Agent卸载
异常场景: UniAccess Agent导致系统中的好多设置打不开 例如:ipv4的协议,注册表,host等等 需要进行删除,亲测有效,及多家答案平凑的 借鉴了这位大神及他里面引用的大神的内容 https://blog.csdn.net/weixin_44476410/article/details/121605455 问题描述 这个进…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...