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

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 视频类&#xff0c;免驱动的。视频流格式有MJPG和YUY2。MJPG是RGB三色通道的。要对三通道进行分屏显示。 代码 import cv2 import numpy as np video …...

Allure 集成 pytest

Allure 是一个强大的测试报告工具&#xff0c;与 pytest 集成可以生成详细的测试报告&#xff0c;包括测试步骤、测试数据、截图、错误堆栈等。 1. 安装 Allure 和相关依赖 安装 pytest-allure-adaptor 插件&#xff1a; pip install allure-pytest确保本地已安装 Allure 工具。…...

【Python】构建智能语音助手:使用Python实现语音识别与合成的全面指南

随着人工智能技术的迅猛发展&#xff0c;语音助手已成为人们日常生活中不可或缺的一部分。从智能手机到智能家居设备&#xff0c;语音交互提供了便捷高效的人机交互方式。本文旨在全面介绍如何利用Python编程语言及其强大的库——SpeechRecognition和gTTS&#xff0c;构建一个基…...

在 Arthas 中调用 Spring Bean 方法

获取 Spring 应用上下文 使用工具类 如果你的项目中有一个工具类实现了 ApplicationContextAware 接口&#xff0c;如 cn.shutdown.pf.utils.SpringContextUtils&#xff0c;可以使用该类获取 ApplicationContext&#xff1a; Component public final class SpringContextUt…...

Nginx入门笔记

Nginx入门笔记 一、Nginx基本概念二、代理1、正向代理2、反向代理 三、准备工作1、CentOS 7安装nginx&#xff08;1&#xff09;. 安装必要的依赖&#xff08;2&#xff09;下载nginx&#xff08;3&#xff09;编译安装&#xff08;4&#xff09;编译并安装 Nginx(5)启动nginx …...

【单片机】实现一个简单的ADC滤波器

实现一个 ADC的滤波器&#xff0c;PT1 滤波器&#xff08;也称为一阶低通滤波器&#xff09;&#xff0c;用于对输入信号进行滤波处理。 typedef struct PT1FilterSettings PT1FilterSettings; struct PT1FilterSettings {//! last Filter output valueuint32_t filtValOld;//…...

开源 vGPU 方案 HAMi 解析

开源 vGPU 方案 HAMi 一、k8s 环境下 GPU 资源管理的现状与问题 &#xff08;一&#xff09;资源感知与绑定 在 k8s 中&#xff0c;资源与节点紧密绑定。对于 GPU 资源&#xff0c;我们依赖 NVIDIA 提供的 device-plugin 来进行感知&#xff0c;并将其上报到 kube-apiserver…...

备考蓝桥杯:顺序表详解(静态顺序表,vector用法)

目录 1.顺序表的概念 2.静态顺序表的实现 总代码 3.stl库动态顺序表vector 测试代码 1.顺序表的概念 要理解顺序表&#xff0c;我们要先了解一下什么是线性表 线性表是n个具有相同特征的数据元素的序列 这就是一个线性表 a1是表头 a4是表尾 a2是a3的前驱 a3是a2的后继 空…...

OA系统如何做好DDOS防护

OA系统如何做好DDOS防护&#xff1f;在数字化办公蔚然成风的当下&#xff0c;OA&#xff08;办公自动化&#xff09;系统作为企业内部管理与协作的神经中枢&#xff0c;其安全性和稳定性直接关系到企业的日常运营效率、信息流通效率以及长远发展。OA系统不仅承载着企业内部的日…...

使用 Python 的 pyttsx3 库进行文本转语音

1. 什么是 pyttsx3&#xff1f; 1.1 pyttsx3 是一个 Python 库&#xff0c;它可以将文本转换为语音。与其他文本转语音库&#xff08;如 gTTS&#xff09;不同&#xff0c;pyttsx3 不依赖于网络服务&#xff0c;它使用本地的 TTS&#xff08;Text-to-Speech&#xff09;引擎&a…...

如何在Windows上编译OpenCV4.7.0

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

【玩转全栈】----Django连接MySQL

阅前先赞&#xff0c;养好习惯&#xff01; 目录 1、ORM框架介绍 选择建议 2、安装mysqlclient 3、创建数据库 4、修改settings&#xff0c;连接数据库 5、对数据库进行操作 创建表 删除表 添加数据 删除数据 修改&#xff08;更新&#xff09;数据&#xff1a; 获取数据 1、OR…...

25/1/4 算法笔记<强化学习> 生成对抗模仿学习

基于生成对抗网络的模仿学习&#xff0c;假设存在一个专家智能体&#xff0c;其策略可以看成最优策略&#xff0c;我们就可以通过直接模仿这个专家在环境中交互的动作数据来训练一个策略&#xff0c;并不需要用到环境提供的奖励信息。 生成对抗模仿学习GAIL实质上就是模仿了专家…...

Flink维表方案选型

Iceberg Iceberg 采用全量预加载数据的方式将维度表数据全部加载到内存中进行关联&#xff0c;虽然可以避免频繁访问外部数据库&#xff0c;但对计算节点的内存消耗很高&#xff0c;不能适用于数量很大的维度表。除此之外&#xff0c;当 Iceberg 维表数据更新后&#xff0c;可…...

Oracle Database 23ai 新特性: UPDATE 和 DELETE 语句的直接联接

Oracle Database 23c 引入了一系列令人振奋的新特性&#xff0c;其中一项尤为引人注目的是对 UPDATE 和 DELETE 语句支持直接联接&#xff08;Direct Join&#xff09;。这一新功能极大地简化了复杂数据操作的实现&#xff0c;提升了性能&#xff0c;并为数据库开发者提供了更强…...

机器学习之随机森林算法实现和特征重要性排名可视化

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

网络安全图谱以及溯源算法

​ 本文提出了一种网络攻击溯源框架&#xff0c;以及一种网络安全知识图谱&#xff0c;该图由六个部分组成&#xff0c;G <H&#xff0c;V&#xff0c;A&#xff0c;E&#xff0c;L&#xff0c;S&#xff0c;R>。 1|11.知识图 ​ 网络知识图由六个部分组成&#xff0c…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战

🧠关键词:Zephyr、BLE、LoRa、混合通信、事件驱动、网关中继、低功耗调度 📌面向读者:希望将 BLE 和 LoRa 结合应用于资产追踪、环境监测、远程数据采集等场景的开发者 📊篇幅预计:5300+ 字 🧭 背景与需求 在许多 IoT 项目中,单一通信方式往往难以兼顾近场数据采集…...

GC1808:高性能音频ADC的卓越之选

在音频处理领域&#xff0c;高质量的音频模数转换器&#xff08;ADC&#xff09;是实现精准音频数字化的关键。GC1808&#xff0c;一款96kHz、24bit立体声音频ADC&#xff0c;以其卓越的性能和高性价比脱颖而出&#xff0c;成为众多音频设备制造商的理想选择。 GC1808集成了64倍…...

World-writable config file /etc/mysql/mysql.conf.d/my.cnf is ignored

https://stackoverflow.com/questions/53741107/mysql-in-docker-on-ubuntu-warning-world-writable-config-file-is-ignored 修改权限 -> 重启mysql # 检查字符集配置 SHOW VARIABLES WHERE Variable_name IN (character_set_server, character_set_database ); --------…...

链结构与工作量证明7️⃣:用 Go 实现比特币的核心机制

链结构与工作量证明:用 Go 实现比特币的核心机制 如果你用 Go 写过区块、算过哈希,也大致理解了非对称加密、数据序列化这些“硬核知识”,那么恭喜你,现在我们终于可以把这些拼成一条完整的“区块链”。 不过别急,这一节我们重点搞懂两件事: 区块之间是怎么连接成“链”…...