【C语言】宏详解(上卷)

news2024/11/26 19:40:00

前言

紧接着预处理详解(上卷),接下来我们来讲宏(隶属于预处理详解系列)。

#define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。

所以,宏其实是有参数的,这是与我们之前的#define定义常量很不同的一点。

宏的声明方式

#define name(parameter-list) stuff

parameter-list是一个由逗号隔开的符号表,它们可能出现在stuff中。

与函数不同的地方在于,宏的参数是没有类型的。

 宏运行把参数列表里的东西替换到内容(stuff)里。

注意:参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

演示:

比如我们现在实现一个宏,求平方。

SQUARE就是宏的名字,n是宏的参数,n*n是宏的内容。可以看到,参数出现在宏的内容中。

编译器对其预处理后是这样的:

#include<stdio.h>
#define SQUARE(n) n*n

int main()
{
	int x = 0;
	scanf("%d", &x);
	int ret = x*x;//替换了
	printf("%d\n", ret);

	return 0;
}

理解:

宏和函数

 我们可以感受到,宏和函数很相似,如果写成函数是这样:

#include<stdio.h>
int square(int n)
{
	return n * n;
}

int main()
{
	int x = 0;
	scanf("%d", &x);
	int ret = square(x);
	printf("%d\n", ret);

	return 0;
}

对于函数来说,要有函数名,参数,返回类型,函数体;对于宏来说,要有宏的名字,参数,宏的体(内容),不过没有参数类型、返回类型。

宏适合完成相对简单的任务。因为当任务复杂时,放进括号(函数的{})里会更方便观看。

宏与括号

现在,如果我们将参数改为5+1,会发生什么?

可以看到我们是得不到想要的结果36的,而是结果为11。 

这是因为它是这么替换的:

#include<stdio.h>
#define SQUARE(n) n*n

int main()
{
	
	int ret = 5 + 1 * 5 + 1;
	printf("%d\n", ret);

	return 0;
}

直接替换,而不是算成6后再替换。

可以这样修改:

所以,在写宏的时候,不要吝啬括号。 

再举一个例子,下面的代码是经不起考验的:

实现一个宏,求一个数的2倍:

我们得不到想要的100,而是得到55。这是因为替换后是这样的:

#include<stdio.h>
#define DOUBLE(x) x+x
int main()
{
	/*int n = 0;
	scanf("%d", &n);*/

	int ret = 10 * 5 + 5;
	printf("%d\n", ret);

	return 0;
}

也是因为是直接替换。

这时应该这样改:

再次重申这个忠告:在写宏的时候一定不要吝啬括号。

用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符(前一个例子)或邻近操作符(后一个例子),产生不可预料的相互作用。

带有副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果

举个例子说明什么是副作用:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = a + 1;//b=11,a=10。无副作用
	int b = ++a;//b=11,a=11。有副作用

	return 0;
}

如果我们的宏的参数是带有副作用的呢?会发生什么呢?

当宏参数在宏的定义中出现超过一次时,如果参数带有副作用,那么在使用这个宏的时候可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

演示:

#include<stdio.h>
#define MAX(x,y) ((x)>(y)?(x):(y))
//写一个宏,求两个数的较大值
int main()
{
	int a = 10;
	int b = 20;
	int m = MAX(a, b);
	printf("%d\n", m);

	return 0;
}

(x)>(y)?(x):(y)是如果(x)大于(y)表达式结果就为(x),否则表达式结果为(y)。

我们可以看到,宏的参数x、y都在宏体内出现了两次。如果现在我们传的是a++,b++,此时算出的m是多少呢?

这是替换后:

int m = ((a++)>(b++)?(a++):(b++));

怎么算呢?

从左向右依次计算,a++是后置++,先试用后++,所以(a++)表达式结果是10,(b++)是20,但是a和b都会++一次,因为10<20,后面的表达式(a++)不会执行,执行的是(b++),b此时已是21,先使用21再++,m得到的是21。真个表达式结束后a变成了11,b变成了22。

 如果写成函数,则是这样的:

a++ b++是先使用再++,所以传给函数形参的值是10和20,最后得到的m也是正确的结果。最后打印的a和b也是分别++一次的结果。

但因为宏使用的是替代到宏体内的方式,如果宏体内同一个参数出现多次,++也会出现多次。这种行为是非常危险的。

所以我们在写宏的时候,最好不要传带有副作用的参数,否则产生的结果可能不是我们希望的。

至此,上卷内容结束,祝阅读愉快,敬请期待下卷^_^

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1805490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

步态控制之足旋转点(Foot Rotation Indicator, FRI)

足旋转点(Foot Rotation Indicator, FRI) 足旋转点是人形机器人步态规划中的一个关键概念,用于描述步态过程中机器人脚部的旋转和稳定性。FRI 可以帮助确定机器人在行走时是否稳定,以及如何调整步态以保持稳定。下面详细介绍FRI的原理,并举例说明其应用。 足旋转点(FRI…

Word多级标题编号不连续、一级标题用大写数字二级以下用阿拉伯数字

Word多级标题编号不连续 &#xff1a; 一级标题用大写数字二级以下用阿拉伯数字&#xff1a;

Qt Designer 生成的 .ui 文件转为 .py 文件并运行

1. 使用使用 PyUIC将 .ui 转 .py &#xff08;1&#xff09;打开命令行终端&#xff08;可以用cmd&#xff0c;或pycharm 下面的 Terminal&#xff09;。 &#xff08;2&#xff09;导航到包含.ui文件的目录。 cd 你的ui文件路径 &#xff08;3&#xff09;运行以下命令来…

Linux -- 了解 vim

目录 vim Linux 怎么编写代码&#xff1f; 了解 vim 的模式 什么是命令模式&#xff1f; 命令模式下 vim 的快捷键&#xff1a; 光标定位&#xff1a; 复制粘贴&#xff1a; 删除及撤销&#xff1a; 注释代码&#xff1a; 什么是底行模式&#xff1f; ​编辑 ​编辑…

Java:110-SpringMVC的底层原理(上篇)

SpringMVC的底层原理 在前面我们学习了SpringMVC的使用&#xff08;67章博客开始&#xff09;&#xff0c;现在开始说明他的原理&#xff08;实际上更多的细节只存在67章博客中&#xff0c;这篇博客只是讲一点深度&#xff0c;重复的东西尽量少说明点&#xff09; MVC 体系结…

【递归、搜索与回溯】DFS | 回溯 | 剪枝

一、经验总结 实际上递归、深度优先搜索&#xff08;DFS&#xff09;、回溯与剪枝研究的是同一类问题&#xff0c;我将其统称为递归深搜算法&#xff0c;其解题步骤大致如下&#xff1a; 画出递归决策树&#xff1a;如果遇到较为复杂的递归题目&#xff0c;可以通过画出决策树明…

hcache缓存查看工具

1、hcache概述 hcache是基于pcstat的&#xff0c;pcstat可以查看某个文件是否被缓存和根据进程pid来查看都缓存了哪些文件。hcache在其基础上增加了查看整个操作系统Cache和根据使用Cache大小排序的特性。官网:https://github.com/silenceshell/hcache 2、hcache安装 2.1下载…

数据又错了-深读实证01

以后我会经常发一些随手写成的“深读实证”&#xff0c;将书稿中的一两个知识点进行展开介绍。 这是第一篇深读实证&#xff0c;涉及的书中知识点有&#xff1a; 我是严肃科普&#xff0c;别夸我观点鲜明&#xff0c;那都是真事&#xff0c;不是观点。云厂商还有大量优化运营提…

深度强化学习+大模型综述Survey on Large Language Model-Enhanced Reinforcement Learning

论文地址&#xff1a;[2404.00282] Survey on Large Language Model-Enhanced Reinforcement Learning: Concept, Taxonomy, and Methods (arxiv.org) 摘要 对 LLM 增强 RL 中现有文献进行了全面的回顾&#xff0c;并总结了其与传统 RL 方法相比的特征&#xff0c;旨在阐明未…

【Linux高级IO】select、poll、epoll

【Linux高级IO】select、poll、epoll toc 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.6.5 前言&#xff1a;本篇博客将会介绍面试重点考察的select、poll、epoll IO: input && Output read && write 应用层read&&write的时候&#xff0c…

nest入门教程

1.介绍&#xff1a; Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用的框架。它使用渐进式 JavaScript&#xff0c;构建并完全支持 TypeScript&#xff08;但仍然允许开发者使用纯 JavaScript 进行编码&#xff09;并结合了 OOP&#xff08;面向对象编程&am…

LLM技术

LLM 是利用深度学习和大数据训练的人工智能系统&#xff0c;专门设计来理解、生成和回应自然语言。这些模型通过分析大量的文本数据来学习语言的结构和用法&#xff0c;从而能够执行各种语言相关任务。以 GPT 系列为代表&#xff0c;LLM 以其在自然语言处理领域的卓越表现&…

【Node.js快速部署opencv项目】图像分类与目标检测

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三连支…

Java Web学习笔记22——前端工程化

实际的前端开发&#xff1a; 前端工程化&#xff1a;是指在企业级的前端项目开发中&#xff0c;把前端开发所需的工具、技术、流程、经验等进行规范化、标准化。 环境准备&#xff1a; vue-cli&#xff1a; 介绍&#xff1a;vue-cli是Vue官方提供的一个脚手架&#xff0c;用于…

推荐云盘哪个好,各有各的优势

选择合适的云盘服务是确保数据安全、便捷分享和高效协作的关键。下面将从多个维度对目前主流的云盘服务进行详细的对比和分析&#xff1a; 速度性能 百度网盘青春版&#xff1a;根据测试&#xff0c;其上传和下载确实不限速&#xff0c;但主要定位是办公人群&#xff0c;适用于…

STM32快速入门(ADC数模转换)

STM32快速入门&#xff08;ADC数模转换&#xff09; 前言 ADC数模转换存在的意义就是将一些温度传感器、各自数据传感器产生的模拟信号转换成方便识别和计算的数字信号。 导航 图24 通用定时器框图&#xff1a; 图片截取自STM32 F1XX中文参考手册。还是以框图为中心&#x…

MySQL—多表查询—标量子查询

一、引言 上篇学习完子查询的概念和分类。 现在来学习第一种子查询——标量子查询。 &#xff08;1&#xff09;标量子查询的基本概念 子查询返回的结果是单个值&#xff0c;也就是一行一列&#xff08;可以是数字、字符串、日期等&#xff09; 是一种最简单的子查询形式&am…

Go 语言的函数详解:语法、用法与最佳实践

在 Go 语言的世界里&#xff0c;函数是构建和维护任何应用程序的基石。不仅因为它们提供了一种将大问题划分为更小、更易管理部分的方法&#xff0c;而且还因为它们在 Go 程序中扮演着至关重要的角色。从简单的工具函数到复杂的系统级调用&#xff0c;理解和利用 Go 的函数特性…

论文阅读:All-In-One Image Restoration for Unknown Corruption

发表时间&#xff1a;2022 cvpr 论文地址&#xff1a;https://openaccess.thecvf.com/content/CVPR2022/papers/Li_All-in-One_Image_Restoration_for_Unknown_Corruption_CVPR_2022_paper.pdf 项目地址&#xff1a;https://github.com/XLearning-SCU/2022-CVPR-AirNet 在本文…

Mysql使用中的性能优化——索引对插入操作的性能影响

当我们往表中插入数据时&#xff0c;如果表中有索引&#xff0c;则会给插入操作增加更多的工作量。带来的好处是可以提升查询效率。但是这种优劣该如何权衡&#xff0c;则需要通过数据对比来提供佐证。本文我们将对比没有索引、有一个普通索引、有一个唯一索引的性能差距。 结…