C语言#define定义宏
目录
一、什么是宏以及宏的声明方式
1.宏常量:
2.宏函数:
二、宏的替换原则
三、宏设计的易犯错误
ERROR1:尾部加分号(当然有些特定需要加了分号,这里说明一般情况)
ERROR2:宏函数定义时,没有保证其参数在表达式中的独立性,以及表达式的独立性
ERROR3:带副作用的宏参数
四、#,##
1.使用# ,把一个宏参数变成对应的字符串
2.使用##,允许宏定义从分离的文本片段创建标识符
五、宏函数特性分析
前言:
宏,每个C语言学习者绕不开的话题,其使用简便又十分易错,稍不注意甚至处处小心仍然容易写出BUG。那么宏是什么,应该如何正确使用(减小使用错误概率)?今天让我们来一探究竟。
一、什么是宏以及宏的声明方式
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
宏的声明方式:
1.宏常量:
#define 宏常量名 宏常量值 ------- 例:#define MAX 1000
说明:
a.宏常量名和普通常量名没什么区别,不过习惯上使用大写字母表示
b.语句不同成分之间用空格隔开,末尾不需要加分号
2.宏函数:
#define 宏函数名(参数列表) 宏函数表达式 ------ 例: #define ADD ( x, y ) ( ( x ) + ( y ) )
ps.上述函数表达式看起来很别扭,但是这是比较安全的写法,下文会进行解释。
说明:
a.宏函数名和普通变量名没什么区别,不过习惯上使用大写字母表示
b.语句不同成分之间用空格隔开,末尾不需要加分号
注意:
参数列表的左括号必须与宏函数名紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为宏函数体的一部分。
二、宏的替换原则
我们在设计宏和使用宏时,往往因为对宏的替换原则不够了解而出现错误,而宏的替换总结起来就是两个字——“生硬”。
宏是在编译的阶段进行的替换,编译器会对代码进行检查,对使用了宏常量名和宏函数名的代码段将对应的常量值或函数表达式直接在原位置处嵌入式替换。
例:
#define MAX 1000
#define ADD( x, y) ( ( x ) + ( y ) )int main()
{
// 替换前printf("%d",MAX);printf("%d",ADD( 1, 2));
// 替换后printf("%d",1000);printf("%d",( ( 1 ) + ( 2 ) ));return 0;
}
这就如同我们进行覆盖式 ctrl + c/v 一样,如此这般确实显得编译器很生硬,但这也是没有办法,毕竟语法规定,同时编译器也不能自作主张的替换,这就要求我们编程者根据特性来好好设计宏了 。
三、宏设计的易犯错误
了解了宏的替换原则,想必对于设计宏犯错误的原因也大概知道了,就是由于生硬的替换而导致逻辑或者语义甚至语法的错误出现。
ERROR1:尾部加分号(当然有些特定需要加了分号,这里说明一般情况)
宏替换后, max = MAX;就变成 max = 1000;; ,这也就导致了在if else语句之间出现了一个空语句,else找不到if而落单了,这很明显是有问题的。
宏替换后,printf("%d",MAX) ;就变成了 printf("%d",1000;) ; ,这一个明眼人都能看出明显问题。
ERROR2:宏函数定义时,没有保证其参数在表达式中的独立性,以及表达式的独立性
这里我们设计一下加法宏函数
这样的结果会是多少呢?想必根据上文提到的替换规则,应该很清楚,发生替换后,变为
5 * 2 + 3 = 13
回答正确!
那如果这样呢?还是13吗?
这时,就昏头了,是先计算宏参数值呢?还是先替换?测试瞧瞧
这是 5 * 1 + 1 + 2 + 1的结果,看来是先直接替换无疑了,这也很体现宏的“生硬”
宏函是仿照函数的,应该和函数基本性质相同才对,而对于函数,函数参数传参时应该先计算后传入,对于函数表达式更是一个整体,全算完了才进行外部动作。
所以对于宏函数的函数表达式,我们应该用括号将参数以及函数表达式的独立性体现出来,于是我们这样定义: #define ADD(x,y) (( x )+( y ))
ERROR3:带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
这里的副作用通常是指对自身进行改变的作用。如:x + 1 与 x++
我们进行替换
结果
在进行判断后,x,y自增,选择结束后y又进行自增,所以这即便给每个参数都确保的独立性,也无法保证其正确。
四、#,##
1.使用# ,把一个宏参数变成对应的字符串
#对于宏,有这么一个技巧,可以把一个宏参数变成对应的字符串:
PRINT("%d", i + 3) 变为 printf("the value of "i + 3" is"“%d” "\n", 1 + 3)
ps.这里还涉及printf字符串拼接的特性printf("haha""nihao") -> printf("hahanihao")
2.使用##,允许宏定义从分离的文本片段创建标识符
这样的连接必须产生一个合法的标识符,否则其结果就是未定义的。
五、宏函数特性分析
通过上文,应该不会有人还觉得宏函数那么好干嘛还要使用普通函数了吧。
a.调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。(创建函数栈桢有内存消耗)所以宏比函数在程序的规模和速度方面更胜一筹。
b.函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏可以适用于整形、长整型、浮点型等可以用于来比较的类型。宏是类型无关的。但也意味着宏没有类型检查,这在一些时候是不安全的体现。
c. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度,增加编译工作量。
d.宏在编译阶段就进行替换,是没法调试的。
4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
综上宏函数适合短小计算,可以替代小型函数,但大型函数也是无能为力,对于类型检查有要求的,可以使用enum枚举常量来替代。
相关文章:

C语言#define定义宏
目录 一、什么是宏以及宏的声明方式 1.宏常量: 2.宏函数: 二、宏的替换原则 三、宏设计的易犯错误 ERROR1:尾部加分号(当然有些特定需要加了分号,这里说明一般情况) ERROR2:宏函数定义时&…...

SpringBoot操作spark处理hdfs文件
SpringBoot操作spark处理hdfs文件 1、导入依赖 <!-- spark依赖--><dependency><groupId>org.apache.spark</groupId><artifactId>spark-core_2.12</artifactId><version>3.2.2</version></dependency><depend…...
消息队列架构、选型、专有名词解释
私人博客传送门 消息队列专有名词解释 | 魔筝炼药师 MQ选型 | 魔筝炼药师 MQ架构 | 魔筝炼药师 MQ顺序消息 | 魔筝炼药师...

用OpenCV实现UVC视频分屏
分屏 OpencvUVC代码验证后话 用OpenCV实现UVC摄像头的视频分屏。 Opencv opencv里有很多视频图像的处理功能。 UVC Usb 视频类,免驱动的。视频流格式有MJPG和YUY2。MJPG是RGB三色通道的。要对三通道进行分屏显示。 代码 import cv2 import numpy as np video …...
Allure 集成 pytest
Allure 是一个强大的测试报告工具,与 pytest 集成可以生成详细的测试报告,包括测试步骤、测试数据、截图、错误堆栈等。 1. 安装 Allure 和相关依赖 安装 pytest-allure-adaptor 插件: pip install allure-pytest确保本地已安装 Allure 工具。…...
【Python】构建智能语音助手:使用Python实现语音识别与合成的全面指南
随着人工智能技术的迅猛发展,语音助手已成为人们日常生活中不可或缺的一部分。从智能手机到智能家居设备,语音交互提供了便捷高效的人机交互方式。本文旨在全面介绍如何利用Python编程语言及其强大的库——SpeechRecognition和gTTS,构建一个基…...
在 Arthas 中调用 Spring Bean 方法
获取 Spring 应用上下文 使用工具类 如果你的项目中有一个工具类实现了 ApplicationContextAware 接口,如 cn.shutdown.pf.utils.SpringContextUtils,可以使用该类获取 ApplicationContext: Component public final class SpringContextUt…...

Nginx入门笔记
Nginx入门笔记 一、Nginx基本概念二、代理1、正向代理2、反向代理 三、准备工作1、CentOS 7安装nginx(1). 安装必要的依赖(2)下载nginx(3)编译安装(4)编译并安装 Nginx(5)启动nginx …...
【单片机】实现一个简单的ADC滤波器
实现一个 ADC的滤波器,PT1 滤波器(也称为一阶低通滤波器),用于对输入信号进行滤波处理。 typedef struct PT1FilterSettings PT1FilterSettings; struct PT1FilterSettings {//! last Filter output valueuint32_t filtValOld;//…...
开源 vGPU 方案 HAMi 解析
开源 vGPU 方案 HAMi 一、k8s 环境下 GPU 资源管理的现状与问题 (一)资源感知与绑定 在 k8s 中,资源与节点紧密绑定。对于 GPU 资源,我们依赖 NVIDIA 提供的 device-plugin 来进行感知,并将其上报到 kube-apiserver…...

备考蓝桥杯:顺序表详解(静态顺序表,vector用法)
目录 1.顺序表的概念 2.静态顺序表的实现 总代码 3.stl库动态顺序表vector 测试代码 1.顺序表的概念 要理解顺序表,我们要先了解一下什么是线性表 线性表是n个具有相同特征的数据元素的序列 这就是一个线性表 a1是表头 a4是表尾 a2是a3的前驱 a3是a2的后继 空…...
OA系统如何做好DDOS防护
OA系统如何做好DDOS防护?在数字化办公蔚然成风的当下,OA(办公自动化)系统作为企业内部管理与协作的神经中枢,其安全性和稳定性直接关系到企业的日常运营效率、信息流通效率以及长远发展。OA系统不仅承载着企业内部的日…...
使用 Python 的 pyttsx3 库进行文本转语音
1. 什么是 pyttsx3? 1.1 pyttsx3 是一个 Python 库,它可以将文本转换为语音。与其他文本转语音库(如 gTTS)不同,pyttsx3 不依赖于网络服务,它使用本地的 TTS(Text-to-Speech)引擎&a…...

如何在Windows上编译OpenCV4.7.0
前言 参考:Win10 下编译 OpenCV 4.7.0详细全过程,包含xfeatures2d 这里在其基础上还出现了一些问题,仅供参考。 正文 一、环境 1、win10 2、cmake-gui 3、opencv4.7.0 4、VS2019 二、编译过程 1、下载需要的文件: 通…...

【玩转全栈】----Django连接MySQL
阅前先赞,养好习惯! 目录 1、ORM框架介绍 选择建议 2、安装mysqlclient 3、创建数据库 4、修改settings,连接数据库 5、对数据库进行操作 创建表 删除表 添加数据 删除数据 修改(更新)数据: 获取数据 1、OR…...
25/1/4 算法笔记<强化学习> 生成对抗模仿学习
基于生成对抗网络的模仿学习,假设存在一个专家智能体,其策略可以看成最优策略,我们就可以通过直接模仿这个专家在环境中交互的动作数据来训练一个策略,并不需要用到环境提供的奖励信息。 生成对抗模仿学习GAIL实质上就是模仿了专家…...
Flink维表方案选型
Iceberg Iceberg 采用全量预加载数据的方式将维度表数据全部加载到内存中进行关联,虽然可以避免频繁访问外部数据库,但对计算节点的内存消耗很高,不能适用于数量很大的维度表。除此之外,当 Iceberg 维表数据更新后,可…...
Oracle Database 23ai 新特性: UPDATE 和 DELETE 语句的直接联接
Oracle Database 23c 引入了一系列令人振奋的新特性,其中一项尤为引人注目的是对 UPDATE 和 DELETE 语句支持直接联接(Direct Join)。这一新功能极大地简化了复杂数据操作的实现,提升了性能,并为数据库开发者提供了更强…...

机器学习之随机森林算法实现和特征重要性排名可视化
随机森林算法实现和特征重要性排名可视化 目录 随机森林算法实现和特征重要性排名可视化1 随机森林算法1.1 概念1.2 主要特点1.3 优缺点1.4 步骤1.5 函数及参数1.5.1 函数导入1.5.2 参数 1.6 特征重要性排名 2 实际代码测试 1 随机森林算法 1.1 概念 是一种基于树模型的集成学…...

网络安全图谱以及溯源算法
本文提出了一种网络攻击溯源框架,以及一种网络安全知识图谱,该图由六个部分组成,G <H,V,A,E,L,S,R>。 1|11.知识图 网络知识图由六个部分组成,…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...