C语言之函数式宏

news2024/12/23 0:46:25

目录

函数和数据类型

函数式宏

函数和函数式宏

函数式宏和对象式宏

不带参数的函数式宏

函数式宏和逗号运算符


函数式宏和函数类似并且比函数更加灵活,下面我们就来学习函数式宏的相关内容。

函数和数据类型

我们来编写一个程序,它能计算出所读取数值的平方,并将结果显示出来,我们先来编写适用于int类型和double类型的函数。

/*整数和浮点数的平方*/
#include<stdio.h>

/*计算int型数的平方值*/
int sqr_int(int x)
{
	return x * x;
}
/*计算double型数的平方值*/
double sqr_double(double x)
{
	return x * x;
}
int mian()
{
	int x;
	double n;
	
	printf("请输入一个整数:");
	scanf("%d", &x);
	printf("该整数的平方是%d\n", sqr_int(x));
	
	printf("请输入一个整数:");
	scanf("%lf", &n);
	printf("该整数的平方是%f\n", sqr_double(n));
	
	return 0;
}

如果我们又想计算其他数据类型的平方呢?比如计算long型数的平方,就得创建出一个sqr_long的函数,如果接二连三的写出这种功能相近,名称相似的函数,程序就会充斥着这种似是而非的函数

下面我们来学习解决办法:函数式宏


函数式宏

函数式宏(function—like macro)较之对象式宏可以进行更为复杂的代换。

#include<stdio.h>

#define sqr(x) ((x) * (x))//计算x平方的函数式宏

int main()
{
	int x;
	double n;
	
	printf("请输入一个整数:");
	scanf("%d", &x);
	printf("该整数的平方是%d\n", sqr(x));
	
	printf("请输入一个整数:");
	scanf("%lf", &n);
	printf("该整数的平方是%f\n", sqr(n));
	
	return 0;
}
//#define 给出的命令如下:

下文中若出现sqr(☺)形式的表达式就将其展开为:

((☺)* (☺))

因此,在调用printf函数时就可以像下面一样展开并执行:

printf("该数的平方是%d\n", ((x) * (x)));

函数和函数式宏

函数和函数式宏的调用看上去相同,但也有以下几个区别:

■函数式宏sqr是在编译时展开并填入程序的,因此只要能使用双目运算符 * 进行乘法运算的数据类型,都能使用函数式宏。

■而函数定义则需要每个形参都定义各自的数据类型,返回值类型也都只有一种,就这点而言,函数较为严格。


 ■函数为我们默默无闻的进行一系列的复杂处理:

☞参数传递(将实参复制给形参)

☞函数调用和函数的返回操作(函数流程的控制)

☞返回值的传递

而函数式宏所做的工作只是宏展开和填入程序,并不执行上述步骤。


 ■根据以上特征,函数式宏或许能使程序的运行速度稍微提高,但是程序自身可能会变得臃肿(如果宏展开式极为复杂,那么在使用到它的所有地方都会填入这些复杂的表达式)。


 ■函数式宏在使用时必须小心,比如sqr((a++)* (a++)),每次展开a的值都会递增两次。在不经意间表达式被执行了两次,导致程序出现了意料之外的结果,我们称这种情况为宏的副作用

注意:在定义和使用函数式宏的时候,要仔细考虑是否会使用产生副作用。

☞将函数版的sqr_int作为sqr_int(a++)调用时,a的值不会调用两次,如果是宏版,则要将sqr(a)和a++分开。


函数式宏和对象式宏

如果在宏名称sqr和紧邻其后的( 之间插入空格,进行如下宏定义:

#define sqr (x) ((x) * (x))

 则sqr会被编译器当做对象式宏,程序中的sqr都会被替换为(x) ((x) * (x))

因此我们在定义函数式宏时,不要在宏名称和(之间插入空格。

 以下是计算二值之和的函数式宏:

#define sum_of(x,y) x + y

我们使用以下语句来调用这个函数式宏

z = sum_of(a, b) * sum_of(c, d);

让我们来看看宏展开式是否符合我们的意愿呢?

z = a + b * c + d;

很显然结果不尽人意,保险起见我们在宏定义时将每个参数以及整个表达式都用括号括起来

#define sum_of(x,y) ((x) + (y))

这样表达式就能正确展开了:

z = (a + b) * (c + d);

不带参数的函数式宏

函数式宏也可以像函数那样进行不带参数的定义,例如下面这个响铃的宏alert()

#define alert() (putchar('\n'))

函数式宏和逗号运算符

下面我们来介绍函数式宏的一个重要使用方法,我们先来看下错误示范:

#include<stdio.h>

#define puts_alert(str) {putchar('\a');  puts(str)}

int main()
{
	int n;
	
	printf("请输入一个整数:");
	scanf("%d", &n);
	
	if(n)
		puts_alert("这个整数不是0");
	else
		puts_alert("这个整数是0");
	
	return 0;
}//本程序在编译时报错,因此不能运行

让我们来分析下原因:

函数式宏put_alert的定义是在puts函数显示字符串str时响铃,只不过这个程序在编译时出错,不能运行。

main函数的if语句展开后如下图所示,if语句会在第一个复合语句{ }处结束,这时因为末尾的 ;会被视为空语句,因此编译器会认为“没有if,为何出现了else”(即使这样,也不能去掉{}否则会出现别的错误)

下面就需要讲到逗号运算符了:

逗号运算符
a,b                                  按顺序判断a和b,整个表达式最终生成b的判断结果
#include<stdio.h>

#define puts_alert(str)  (putchar('\a'),  puts(str))

int main()
{
	int n;
	
	printf("请输入一个整数:");
	scanf("%d", &n);
	
	if(n)
		puts_alert("这个整数不是0");
	else
		puts_alert("这个整数是0");
	
	return 0;
}

一般由逗号运算符连接的两个表达式“a, b”在语法上可以视为一个表达式(其实不仅限于逗号运算符,只要是由运算符连接的多个表达式,例如“a + b”,都可以视为一个表达式),因此在本程序中if语句在语法上就是正确的。

 如果宏定义中要代换两个以上的表达式,则使用逗号运算符连接,使其在语法上构成一个表达式。

 我们对逗号运算符是怎么执行的来具体说明下:

对于逗号运算符“a, b”,会按顺序判断表达式a和b。对左侧的a仅进行判断,判断结果会被省略去,对于右侧的表达式b进行判断所得到的类型和值,就是逗号表达式“a, b”的类型和值。

例如:i=1,j=5

运行x = (++i, ++j),则i和j的值都会递增,但是递增后j的值会被赋值给x。


感觉基本数据类型中的整型和字符型、浮点型的知识点好复杂啊!!!

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

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

相关文章

python蓝桥杯的回形取数

#来源于蓝桥杯的训练 题号是用户登录https://www.lanqiao.cn/problems/1517/learning/?page1&first_category_id1&problem_id1517 根据题目描述可以知道&#xff0c;我们传入的是一个矩阵。 在这里我们使用列表来实现矩阵。 那么&#xff0c;我们直接看代码 dir …

超详细 | 黏菌算法原理、实现及其改进与利用(Matlab/Python)

测试函数为F15 在MATLAB中执行程序结果如下&#xff1a; 在Python中执行程序结果如下&#xff1a; 众所周知&#xff0c;麻雀搜索算法SSA是2020年由东华大学沈波教授团队提出[1]的一种性能十分优异的优化算法&#xff0c;而最近作者发现&#xff0c;在2020年还提出了一个优…

腾讯云:AI云探索之路

随着科技的飞速发展&#xff0c;人工智能(AI)云计算领域日益显现出其巨大的潜力和价值。在这个充满挑战和机遇的领域&#xff0c;腾讯云凭借其卓越的技术和创新能力&#xff0c;取得了令人瞩目的成果。本文将深入探讨腾讯云在AI云计算领域的优势&#xff0c;以及其为人工智能发…

Post Json数据与Form表单数据转换器

具体请访问&#xff1a;在线Json转Form表单参数工具

python绘制gif动图--避免收费拒绝水印

在cp源码解读-CSDN博客文章浏览阅读341次,点赞4次,收藏6次。coreutils:cp源代码分析https://blog.csdn.net/zhaiminlove/article/details/135026160中为了直观的表达文件是如何一步步的copy的,就想到了做一个gif,但是从来没弄过,一下有点麻爪了。 首先想到的是百度、Goog…

孩子都能学会的FPGA:第三十三课——用FPGA实现一个通用的SPI主机接收模块

&#xff08;原创声明&#xff1a;该文是作者的原创&#xff0c;面向对象是FPGA入门者&#xff0c;后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门&#xff0c;作者不光让大家知其然&#xff0c;还要让大家知其所以然&#xff01;每个工程作者都搭建了全自动化的仿…

C语言指针4

1. #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h>int main() {int a 10;int* p &a;//一级指针int** pp &p;//二级指针return 0; }上述代码中p的类型是int* pp的类型是int** 2.int* arr[5]; 数组arr的每个元素是整形指针 3.定义一个变量时,去掉变…

报数游戏C语言

分析:掌握数字移动的规律&#xff0c;以及判断&#xff0c;我们可以用一个二维数组来记录每一个人说的数字&#xff0c;就像第一张图片一样&#xff0c;西安向右边移动&#xff0c;再向左下移动&#xff0c;再向左边移动&#xff0c;在向右边移动&#xff0c;在可以用一个数组来…

3 - Electron BrowserWindow对象 关于窗口

优雅的打开应用~ 当加载缓慢&#xff0c;打开应用的一瞬间会出现白屏&#xff0c;以下方法可以解决 const mainWindow new BrowserWindow({ show: false }) mainWindow.once(ready-to-show, () > {mainWindow.show() }) 设置背景颜色 const win new BrowserWindow({ b…

比特币即自由

号外&#xff1a;教链内参12.15《疯狂的铭文》 文 | Ross Ulbricht. 原文标题&#xff1a;Bitcoin Equals Freedom. 2019.9.25 在中本聪发明比特币后的头一年左右&#xff0c;发生了一些特别的事情&#xff0c;不仅没有人预料到&#xff0c;甚至很多人认为不可能。试着想象一下…

【Python从入门到进阶】44、Scrapy的基本介绍和安装

接上篇《43.验证码识别工具结合requests的使用》 上一篇我们学习了如何使用验证码识别工具进行登录验证的自动识别。本篇我们开启一个新的章节&#xff0c;来学习一下快速、高层次的屏幕抓取和web抓取框架Scrapy。 一、Scrapy框架的背景和特点 Scrapy框架是一个为了爬取网站数…

Python:Jupyter

Jupyter是一个开源的交互式计算环境&#xff0c;由Fernando Perez和Brian Granger于2014年创立。它提供了一种方便的方式来展示、共享和探索数据&#xff0c;并且可以与多种编程语言和数据格式进行交互。Jupyter的历史可以追溯到2001年&#xff0c;当时Fernando Perez正在使用P…

开源免费图床Lychee本地部署搭建个人云图床并公网访问【内网穿透】

文章目录 1.前言2. Lychee网站搭建2.1. Lychee下载和安装2.2 Lychee网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 图床作为图片集中存放的服务网站&#xff0c;可以看做是云存储的一部分&#xff0c;既可…

LeetCode(64)分隔链表【链表】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 分隔链表 1.题目 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示…

加油站“变身”快充站,探讨充电新模式——安科瑞 顾烊宇

摘要&#xff1a;新能源汽车规模化发展的同时&#xff0c;充电不便利的痛点愈发明显。在未来的新能源汽车行业发展当中&#xff0c;充电的矛盾要远远大于造车的矛盾&#xff0c;解决好充电的问题成为电动汽车行业发展的一个突出问题。解决充电补能问题&#xff0c;重要的方式之…

Python实现员工管理系统(Django页面版 ) 六

本篇博客主要实现用户账号管理&#xff0c;这与之前的账号管理不同&#xff0c;之前的账号管理你可以理解为公司在外面买的一些手机号然后需要发放给员工做内部使用&#xff0c;而本篇博客的用户账号管理主要是为了后续的登录网页实现&#xff0c;那么我们开始今天的项目实现吧…

栈和队列的实现(Java篇)

文章目录 一、栈的概念二、栈的实现2.1压栈(push)2.2出栈(pop)2.3获取栈顶元素(peek)2.4判断栈是否为空(isEmpty)栈的实现测试 三、队列的概念四、队列的实现4.1入队(offer)4.2出队(poll)4.3判断队列是否为空4.4获取对头元素队列的实现测试 五、循环队列5.1入队5.2出队5.3获取队…

手把手教你Linux查找Java的安装目录并设置环境变量以及Linux下执行javac未找到命令的保姆级教学

查找Java的安装目录 输入 java -version&#xff0c;查看是否成功安装Java 输入 which java&#xff0c;查看Java的执行路径 输入 ls -lrt /usr/bin/java 输入 ls -lrt /etc/alternatives/java&#xff0c;/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64 就是J…

系列七、函数

一、函数 1.1、概述 函数 是指一段可以直接被另一段程序调用的程序或代码。 也就意味着&#xff0c;这一段程序或代码MySQL中已经为我们提供好了&#xff0c;我们要做的就是在合适的业务场景调用对应的函数完成相应的业务需求即可。 1.2、分类 按照业务分类&#xff0c;MySQL中…

centOS安装bochsXshell连接centos启动可视化界面

centOS安装bochs 参考&#xff1a;https://blog.csdn.net/muzi_since/article/details/102559187 首先安装依赖环境&#xff1a; yum install gtk2 gtk2-devel yum install libXt libXt-devel yum install libXpm libXpm-devel yum install SDL SDL-devel yum install libXr…