当前位置: 首页 > 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…...

【万字文档+源码】基于springboot与vue个人财务管理系统-计算机设计学习项目

基于springboot与vue个人财务管理系统1.项目简介 基于springboot与vue个人财务管理系统管理员功能有个人中心&#xff0c;用户管理&#xff0c;账单类型管理。用户功能有个人中心&#xff0c;收入账单管理&#xff0c;支出账单管理&#xff0c;负债管理&#xff0c;财务报表管理…...

CET中电技术如何助光伏企业在“四可“时代抢占先机?

2026年&#xff0c;"十五五"规划开局之年&#xff0c;新能源行业正经历一场深刻的变革。从2025年5月30日136号文推动投资主体转变&#xff0c;到2026年1月30日114号文将"四可"能力从试点推广期正式升级为政策强制标准&#xff0c;分布式光伏的并网逻辑已被…...

LeetCode 102. 二叉树的层序遍历 详细技术解析

LeetCode 102. 二叉树的层序遍历 详细技术解析本文针对 LeetCode 102. 二叉树的层序遍历 问题&#xff0c;从题目解析、核心思路、代码实现、边界处理到面试拓展&#xff0c;进行全方位拆解&#xff0c;适合算法入门及进阶开发者阅读&#xff0c;附完整可运行代码、测试案例及避…...

PINCE代码注入实战:.so文件注入完全指南

PINCE代码注入实战&#xff1a;.so文件注入完全指南 【免费下载链接】PINCE Reverse engineering tool for linux games 项目地址: https://gitcode.com/gh_mirrors/pi/PINCE PINCE是一款强大的Linux游戏逆向工程工具&#xff0c;专注于动态链接库注入技术&#xff0c;为…...

STM32F103C8T6:基于蓝牙指令的舵机角度精确控制

1. 项目背景与应用场景 想象一下这样的场景&#xff1a;早晨醒来&#xff0c;你躺在床上一键遥控窗帘缓缓打开到45度角&#xff0c;让阳光刚好洒在床脚&#xff1b;或者通过手机APP远程调节摄像头云台&#xff0c;让监控视角精确对准门口快递柜。这些看似简单的智能家居功能&am…...

uniApp相机、存储、电话权限申请全攻略:告别频繁弹窗,提升用户体验

uniApp权限管理艺术&#xff1a;优雅实现相机、存储、电话权限的智能授权策略 在移动应用开发中&#xff0c;权限管理一直是开发者与用户之间的微妙博弈。过于频繁的权限请求会引发用户反感&#xff0c;而缺乏透明度的权限获取又可能导致应用商店审核失败。如何在uniApp框架下构…...

DDrawCompat深度解析:让经典DirectX游戏在现代Windows重焕生机

DDrawCompat深度解析&#xff1a;让经典DirectX游戏在现代Windows重焕生机 【免费下载链接】DDrawCompat DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11 项目地址: https://gitcode.com/gh_mirrors/d…...

Linux内核配置入门:手把手教你玩转make menuconfig图形化界面

Linux内核配置入门&#xff1a;手把手教你玩转make menuconfig图形化界面 在Linux内核开发的世界里&#xff0c;内核配置是一个绕不开的关键环节。对于初学者来说&#xff0c;面对庞大的内核源代码和复杂的配置选项&#xff0c;往往会感到无从下手。而make menuconfig作为Linux…...

GLM-4.1V-9B-Base部署案例:高校AI通识课实验平台快速搭建实践

GLM-4.1V-9B-Base部署案例&#xff1a;高校AI通识课实验平台快速搭建实践 1. 项目背景与需求分析 高校AI通识课程面临一个普遍挑战&#xff1a;如何让学生直观理解多模态AI的能力&#xff1f;传统教学往往停留在理论讲解或简单演示层面&#xff0c;学生缺乏动手实践机会。GLM…...

梯度下降为什么总往‘下坡’走?用Python代码直观理解负梯度的奥秘

梯度下降为什么总往‘下坡’走&#xff1f;用Python代码直观理解负梯度的奥秘 想象你站在一座云雾缭绕的山丘上&#xff0c;手拿一张只能显示当前位置坡度的手绘地图。你的目标是找到下山最快的路径——这恰恰是梯度下降算法要解决的核心问题。对于机器学习初学者而言&#xff…...