C语言之函数式宏

目录

函数和数据类型

函数式宏

函数和函数式宏

函数式宏和对象式宏

不带参数的函数式宏

函数式宏和逗号运算符


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

函数和数据类型

我们来编写一个程序,它能计算出所读取数值的平方,并将结果显示出来,我们先来编写适用于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");elseputs_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");elseputs_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.mzph.cn/news/228013.shtml

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

相关文章

Jetpack Compose开发一个Android WiFi导航应用

在以前的一篇文章构建一个WIFI室内定位系统_wifi定位系统-CSDN博客中&#xff0c;我介绍了如何用Android来测量WiFi信号&#xff0c;上传到服务器进行分析后&#xff0c;生成室内不同地方的WiFi指纹&#xff0c;从而帮助进行室内导航。当时我是用的HTML5的技术来快速开发一个An…

QQ邮箱发送工具类的实现

我们在日常开发中&#xff0c;需要实现一个对邮箱的发送&#xff0c;今天就实现邮箱的发送工具类&#xff0c;只需要一些注册邮箱之后的配置即可&#xff0c;我这边使用的是qq邮箱 0.加上依赖 <!--邮箱--><dependency><groupId>org.springframework.boot&l…

基于vue开发 - 编写登录页面样式

vue创建项目&#xff0c;使用可视化界面安装插件-CSDN博客 使用vue UI安装路由插件-CSDN博客 基于vue开发-创建登录页-CSDN博客 在src/views文件夹中创建登录页面login.vue&#xff0c;在router/index.js文件中加入登录页的路由&#xff0c;然后在浏览器中输入登录页的路径就…

FL Studio终身永久2024中文版下载安装详细操作图文步骤教程

FL Studio2024版是一款在国内非常受欢迎的多功能音频处理软件&#xff0c;我们可以通过这款软件来对多种不同格式的音频文件来进行编辑处理。而且FL Studio 2024版还为用户们准备了超多的音乐乐器伴奏&#xff0c;我们可以直接一键调取自己需要的音调。 FL Studio 2024版不仅拥…

算法设计基础——综合

算法设计基础中最基础的几种算法&#xff1a;分治法、减治法、贪心法、动态规划法、回溯法基本都掌握后&#xff0c;我们现在可以对这些算法做整体的比较&#xff0c;本次实验使用蛮力法、动态规划法、回溯法来求解0/1背包问题&#xff0c;来比较各个算法的优劣。 1. 蛮力法 …

代码随想录27期|Python|Day16|二叉树|104.二叉树的最大深度|111.二叉树的最小深度|222.完全二叉树的节点个数

二叉树专题&#xff0c;重点掌握后续的递归和中间节点的处理。 104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 本题在前一章已经解决了层序遍历的解法&#xff0c;现在来聊一下递归法。 首先需要明确两个概念&#xff1a;深度和高度。&#xff08;注意&…

抠图软件哪个好用?什么软件可以抠图换背景?

抠图软件哪个好用&#xff1f;在图片处理中&#xff0c;抠图换背景是一项常见的操作。很多新手可能会对此感到困惑&#xff0c;不知道应该使用什么软件来进行抠图换景。实际上&#xff0c;现在市面上有很多图片处理软件都具备抠图换背景的功能&#xff0c;每款软件都有其优缺点…

LVS负载均衡群集部署 DR模式

目录 DR模式直接路由 LVS-DR工作原理 LVS-DR 数据包流向分析 DR 模式的特点 DR模式 LVS负载均衡群集部署 DR模式直接路由 Direct Routing&#xff0c;简称DR模式&#xff0c;采用半开放式的网络结构&#xff0c;与TUN模式的结构类似&#xff0c;但各节点并不是分散在各地…

c语言链表的基本操作

在C语言中&#xff0c;链表是一种常见的数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含一个数据元素和一个指向下一个节点的指针。链表的基本操作包括创建、插入、删除和遍历等。 下面是一个简单的链表节点结构体定义&#xff1a; struct Node { int da…

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

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

2. 套圈(分治)

题目 Have you ever played quoit in a playground? Quoit is a game in which flat rings are pitched at some toys, with all the toys encircled awarded. In the field of Cyberground, the position of each toy is fixed, and the ring is carefully designed so it c…

搭建消息时光机:深入探究RabbitMQ_recent_history_exchange在Spring Boot中的应用【RabbitMQ实战 二】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 搭建消息时光机&#xff1a;深入探究RabbitMQ_recent_history_exchange在Spring Boot中的应用 引言前言第一&#xff1a;开启插件支持第二&#xff1a;springboot整合第三&#xff1a;效果展示交换机属…

locust 压测 websocket

* 安装 python 3.8 https://www.python.org/ py --version * 安装 locust pip install locust2.5.1 -i http://pypi.douban.com/simple/ pip install locust2.5.1 -i https://pypi.mirrors.ustc.edu.cn/simple/ locust -V 备注&#xff1a;-i 是切换下载源 * 安装依赖 pip ins…

Electron框架:构建跨平台桌面应用的终极解决方案

文章目录 一、Electron框架简介二、Electron框架的优势1. 开发效率高2. 跨平台性能好3. 易于维护4. 强大的原生能力 三、如何使用Electron框架快速开发跨平台桌面应用1. 安装Electron2. 创建项目文件夹3. 编写主进程代码4. 编写界面代码5. 运行应用 《Electron入门与实战》编辑…

《软件方法》2023版1.1利润=需求-设计1.2 ABCD工作流

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 第1章 建模和UML 牵着你走进傍晚的风里&#xff0c;看见万家灯火下面平凡的秘密。 《情歌唱晚》&#xff1b;词&#xff1a;黄群&#xff0c;曲&#xff1a;黄群&#xff0c;唱&#…

word文档实现“目录索引中标题加粗、前导符(...)和页码不加粗”效果

文章目录 1 展示论文模板需要呈现的效果2 所遇到的问题2.1 情形1&#xff1a;当更新整个目录后&#xff0c;目录中的所有文字都不加粗2.2 情形2&#xff1a;无法单独选中文字部分&#xff0c;如果相对文字部分加粗&#xff0c;则前导符和页码也会同时加粗 3 解决步骤3.1 步骤1&…

CIDR(无类域间路由)与VLSM(可变长度子网掩码)的区别

CIDR和VLSM的介绍 CIDR CIDR&#xff08;Classless Inter-Domain Routing&#xff0c;无类域间路由&#xff09;是一种用于对互联网协议&#xff08;IP&#xff09;地址进行聚合和分配的标准。CIDR的引入旨在解决IPv4地址空间的不足和低效分配的问题。在传统的IP地址规划中&a…

关键点检测之修改labelme标注的json中类别名

import json import os import shutil#source_dir表示数据扩增之后的文件夹路径&#xff0c;此时标注的是多分类的标签 #new_dir表示转化之后得到的二分类文件夹def to2class():#json存放路径source_dir r1#json保存路径new_dir r1for i in os.listdir(source_dir):if i.ends…

文本聚类——文本相似度(聚类算法基本概念)

一、文本相似度 1. 度量指标&#xff1a; 两个文本对象之间的相似度两个文本集合之间的相似度文本对象与集合之间的相似度 2. 样本间的相似度 基于距离的度量&#xff1a; 欧氏距离 曼哈顿距离 切比雪夫距离 闵可夫斯基距离 马氏距离 杰卡德距离 基于夹角余弦的度量 公式…

银行数字化转型导师坚鹏:银行数字化转型正在重塑您的工作

您好&#xff0c;我是银行数字化转型导师坚鹏。坚持知行果合一&#xff0c;赋能数字化转型&#xff01;非常荣幸和您分享关于银行数字化转型如何影响老百姓工作的一些思考。 您知道吗&#xff1f;银行数字化转型给您的工作方式带来新变化、新趋势、新潮流啦&#xff01;在这个…