C/C++:在#define中使用参数
文章目录
- 在#define中使用参数
- 参考资料
在#define中使用参数
在#define中使用参数可以创建外形和作用与函数类似的类函数宏。带有
参数的宏看上去很像函数,因为这样的宏也使用圆括号。类函数宏定义的圆
括号中可以有一个或多个参数,随后这些参数出现在替换体中,如图所
示。

下面是一个类函数宏的示例:
#define SQUARE(X) X*X
在程序中可以这样用:
z = SQUARE(2);
这看上去像函数调用,但是它的行为和函数调用完全不同。程序示了类函数宏和另一个宏的用法。该示例中有一些陷阱,请仔细阅读。
/* mac_arg.c -- 带参数的宏 */
#include <stdio.h>
#define SQUARE(X) X*X
#define PR(X) printf("The result is %d.\n", X)
int main(void)
{
int x = 5;
int z;
printf("x = %d\n", x);
z = SQUARE(x);
printf("Evaluating SQUARE(x): ");
PR(z);
z = SQUARE(2);
printf("Evaluating SQUARE(2): ");
PR(z);
printf("Evaluating SQUARE(x+2): ");
PR(SQUARE(x + 2));
printf("Evaluating 100/SQUARE(2): ");
PR(100 / SQUARE(2));
printf("x is %d.\n", x);
printf("Evaluating SQUARE(++x): ");
PR(SQUARE(++x));
printf("After incrementing, x is %x.\n", x);
return 0;
}
这里,SQUARE 是宏标识符,SQUARE(X)中的 X 是宏参数,X * X 是替
换列表。程序清单 16.2 中出现SQUARE(X)的地方都会被X * X替换。这与前
面的示例不同,使用该宏时,既可以用X,也可以用其他符号。宏定义中的
X由宏调用中的符号代替。因此,SQUARE(2)替换为2 * 2,X实际上起到参数
的作用。
然而,稍后你将看到,宏参数与函数参数不完全相同。下面是程序的输
出。注意有些内容可能与我们的预期不符。实际上,你的编译器输出甚至与
下面的结果完全不同。
x = 5
Evaluating SQUARE(x): The result is 25.
Evaluating SQUARE(2): The result is 4.
Evaluating SQUARE(x+2): The result is 17.
Evaluating 100/SQUARE(2): The result is 100.
x is 5.
Evaluating SQUARE(++x): The result is 42.
After incrementing, x is 7.
前两行与预期相符,但是接下来的结果有点奇怪。程序中设置x的值为
5,你可能认为SQUARE(x+2)应该是 7 * 7,即 49。
但是,输出的结果是 17,这不是一个平方值!导致这样结果的原因是,我们前面提到过,预处理器不做计算、不求值,只替换字符序列。预处理器把出现x的地方都替换成x+2。因此,x * x变成了x+2*x+2。如果x为5,那么该表达式的值为:
5+2*5+2 = 5 + 10 + 2 = 17
该例演示了函数调用和宏调用的重要区别。函数调用在程序运行时把参
数的值传递给函数。宏调用在编译之前把参数记号传递给程序。这两个不同
的过程发生在不同时期。是否可以修改宏定义让SQUARE(x+2)得36?
当然可以,要多加几个圆括号:
#define SQUARE(x) (x)*(x)
现在SQUARE(x+2)变成了(x+2)*(x+2),在替换字符串中使用圆括号就得
到符合预期的乘法运算。
但是,这并未解决所有的问题。下面的输出行:
100/SQUARE(2)
将变成:
100/2*2
根据优先级规则,从左往右对表达式求值:
(100/2)2,即502,得100。把SQUARE(x)定义为下面的形式可以解决这种混乱:
#define SQUARE(x) (x*x)
这样修改定义后得100/(2*2),即100/4,得25。
要处理前面的两种情况,要这样定义:
#define SQUARE(x) ((x)*(x))
因此,必要时要使用足够多的圆括号来确保运算和结合的正确顺序。
尽管如此,这样做还是无法避免程序中最后一种情况的问题。
SQUARE(++x)变成了++x*++x,递增了两次x,一次在乘法运算之前,一次
在乘法运算之后:
++x*++x = 6*7 = 42
由于标准并未对这类运算规定顺序,所以有些编译器得 76。而有些编
译器可能在乘法运算之前已经递增了x,所以77得49。在C标准中,对该表
达式求值的这种情况称为未定义行为。无论哪种情况,x的开始值都是5,虽
然从代码上看只递增了一次,但是x的最终值是7。
解决这个问题最简单的方法是,避免用++x 作为宏参数。一般而言,不
要在宏中使用递增或递减运算符。但是,++x可作为函数参数,因为编译器
会对++x求值得5后,再把5传递给函数。
参考资料
《C Primer Plus》
相关文章:
C/C++:在#define中使用参数
文章目录 在#define中使用参数参考资料 在#define中使用参数 在#define中使用参数可以创建外形和作用与函数类似的类函数宏。带有 参数的宏看上去很像函数,因为这样的宏也使用圆括号。类函数宏定义的圆 括号中可以有一个或多个参数,随后这些参数出现在替…...
Hive 查询优化
Hive 查询优化 -- 本地 set mapreduce.framework.namelocal; set hive.exec.mode.local.autotrue; set mapperd.job.trackerlocal; -- yarn set mapreduce.framework.nameyarn; set hive.exec.mode.local.autofalse; set mapperd.job.trackeryarn-- 向量模式 set hive.vectori…...
【Java 进阶篇】JQuery 案例:优雅的隔行换色
在前端的设计中,页面的美观性是至关重要的。而其中一个简单而实用的设计技巧就是隔行换色。通过巧妙地使用 JQuery,我们可以轻松地实现这一效果,为网页增添一份优雅。本篇博客将详细解析 JQuery 隔行换色的实现原理和应用场景,让我…...
Redis 常用的类型和 API
前言 在当今的软件开发中,数据存储与操作是至关重要的一部分。为了满足日益增长的数据需求和对性能的追求,出现了许多不同类型的数据库。其中,Redis 作为一种基于内存且高性能的键值存储数据库,因其快速的读取速度、丰富的数据结…...
在qt的设计师界面没有QVTKOpenGLWidget这个类,只有QOpenGLWidget,那么我们如何得到QVTKOpenGLWidget呢?
文章目录 前言不过,时过境迁,QVTKOpenGLWidget用的越来越少,官方推荐使用qvtkopengnativewidget代替QVTKOpenGLWidget 前言 在qt的设计师界面没有QVTKOpenGLWidget这个类,只有QOpenGLWidget,我们要使用QVTKOpenGLWidget,那么我们如何得到QVTKOpenGLWidget呢? 不过,时过境迁,Q…...
力扣每日一道系列 --- LeetCode 138. 随机链表的复制
📷 江池俊: 个人主页 🔥个人专栏: ✅数据结构探索 ✅LeetCode每日一道 🌅 有航道的人,再渺小也不会迷途。 LeetCode 138. 随机链表的复制 给你一个长度为 n 的链表,每个节点包含一个额外增加…...
无人零售:创新优势与广阔前景
无人零售:创新优势与广阔前景 无人零售在创新方面具有优势。相比发展较为成熟的欧洲和日本的自动贩卖机市场,中国的无人零售市场人均占有量较少,这表明该市场具有广阔的前景和巨大的市场潜力。 此外,无人零售涉及到许多相关行业&…...
【华为OD题库-022】阿里巴巴找黄金宝箱(IV)-java
题目 一贫如洗的椎夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0-N的子,每个箱子上面有一个数字,箱子排列成一个环,编号最大的箱子的下一个是编号为0的箱子。请输出每个箱子贴的数字之后的第…...
Linux 图形界面配置RAID
目录 RAID 1 配置 RAID 5配置 , RAID 配置起来要比 LVM 方便,因为它不像 LVM 那样分了物理卷、卷组和逻辑卷三层,而且每层都需要配置。我们在图形安装界面中配置 RAID 1和 RAID 5,先来看看 RAID 1 的配置方法。 RAID 1 配置 配置 RAID 1…...
(脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别)、( 什么是qps,tps,并发量,pv,uv)、(什么是接口幂等性问题,如何解决?)
1 脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别是什么? 2 什么是qps,tps,并发量,pv,uv 3 什么是接口幂等性问题,如何解决? 1 脏读,不可重复读…...
安全通信网络(设备和技术注解)
网络安全等级保护相关标准参考《GB/T 22239-2019 网络安全等级保护基本要求》和《GB/T 28448-2019 网络安全等级保护测评要求》 密码应用安全性相关标准参考《GB/T 39786-2021 信息系统密码应用基本要求》和《GM/T 0115-2021 信息系统密码应用测评要求》 1网络架构 1.1保证网络…...
深度学习_12_softmax_图片识别优化版代码
因为图片识别很多代码都包装在d2l库里了,直接调用就行了 完整代码: import torch from torch import nn from d2l import torch as d2l"获取训练集&获取检测集" batch_size 256 train_iter, test_iter d2l.load_data_fashion_mnist(ba…...
element-ui设置下拉选择切换必填和非必填
1、<el-form-item label="区域" prop="areaCode" :required="isHaveTo"><el-select v-model="form.areaCode" placeholder="请选择区域" clearable size="small"><el-option v-for="dict in …...
Linux的命令——关于操作用户及用户组的命令
目录 1.Linux的命令格式 2.用户与用户组管理 2.1用户管理 添加用户 设置用户密码 删除用户 修改用户 2.2用户组管理 新增用户组 删除用户组 修改用户组属性 用户组切换 用户组管理 用户切换 1. su 2.sudo 1.Linux的命令格式 Linux系统中几乎所有操作࿰…...
pycharm 设置多级跳转SSH
打开本地终端并运行: ssh -L <local_port>:<target_server_ip>:22 <proxy_server_user><proxy_server_ip>运行完之后就应该已经连接上proxy (Optional) 可以再开一个终端测试一下: ssh -p <local_port> <target_server_user&g…...
LeetCode 189.轮转数组(三种方法解决)
文章目录 题目暴力求解空间换时间三段逆置总结 题目 LeetCode 189.轮转数组 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5…...
GB28181设备对接视频流的流程
搭建CG28181 服务端,也即 SIP Server,这正是我们要实现的。实现CG28181服务端可以借助于现有的开源库 PJSIP,具体的实现步骤如下: 1、启动GB28181服务端,接收客户端消息请求 bool Init(std::string concat, int logL…...
类属性修改(为什么python类不具备被赋值能力?)
为什么python类不具备被赋值能力?,用魔术方法收集实参,在类中可以定义方法处理实际参数,实现对类“赋值”。 (笔记模板由python脚本于2023年11月15日 12:45:27创建,本篇笔记适合初通Python类class的coder翻阅) 【学习的…...
uniapp App端 解决input@input事件动态修改值不生效的问题
解决方法 1.延迟修改,利用setTimeout 2.异步修改,利用this.$nextTick <template><view><input v-modal"num" type"number" placeholder"请输入" :maxlength"3" input"onInputOne" …...
ELK分布式日志
ELK是指Elasticsearch、Logstash和Kibana三个开源软件的集合,用于构建分布式日志处理系统。 Elasticsearch是一款基于Lucene搜索引擎库的分布式全文搜索和分析引擎,支持多种数据类型的存储、搜索和分析,常用于日志分析、安全监控等领域。 L…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
