详解#define

我们要知道,#define后面定义的标识符只进行替换而不进行计算,我们不能根据惯性自动给它计算了,这样可能会出错。


目录

1.关于#define

1.1#define定义标识符

1.2#define定义宏

1.3#define的替换规则

2.#和##

1.#

2.##

3.带副作用的宏参数

4.宏和函数的比较

5.命名的约定

6.#undef:可以移除宏定义


1.关于#define

1.1#define定义标识符

使用格式:#define name stuff

例如:#define MAX  1000   (这个是我们经常用到的)

          #define reg   register    为register创建一个简短的名字reg

          #define do_forever  for(  ; ; )   用更形象的符号来替换一种实现

          #define CASE break;case    写case语句的时候自动把break写上

我们可以举个代码例子来理解

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>//CASE break 来举例#define CASE break;case//它的意思就是把原来case的位置,替换为break;case
int main()
{int input = 0;int flag = 0;printf("请选择:>");scanf("%d ", &input);switch (input){case 1:flag = 1;CASE 2:  flag = 2;CASE 3:flag = 3;CASE 4:flag = 4;default:break;}printf("flag=%d\n", flag);return 0;
}

我们可以看到,对应CASE的位置被替换为 break;case  这样我们就不必每次都写break,因为我们有时会忘记写break,这样就很方便了,是不是很奇妙。

还有一点,在define定义标识符的时候,后面一般不要加“ ;”有时会出错误

还记得我们以前说过的悬空else吗,我们来举个例子:

#define MAX 100;int main()
{int m = 0;scanf("%d", &m);if (m >= 0)m = MAX;elsem = -1;printf("%d\n", m);return 0;
}

1.2#define定义宏

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

2.宏的申明方式:#define  name(parament_list) stuff

其中: name(parament_list)是指宏的参数

        parament_list是一个由逗号隔开的符号表,它可能出现在stuff中

        stuff是宏的内容

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

我们看个例子:

#define SQUARE(x) x*xint main()
{printf("%d\n", SQUARE(5));printf("%f\n", SQUARE(5.0));return 0;
}

但是这种定义方式存在隐患,我们之前说过#define定义的内容只进行替换而不计算,如果此时,我们把5改为5+1,它的值又是多少呢?

我们不妨先猜测一下,相信大多数人都回答36,但我们试着编译一下,发现结果和我们想象中不同

#define SQUARE(x) x*xint main()
{printf("%d\n", SQUARE(5+1));return 0;
}

我们发现结果是11,并不是我们想想中的36,我们来解释一下

SQUARE(5+1)就是 5+1*5+1 =11

所以此时,我们又得到了几个注意点:

在写宏时,我们要勇于加括号,防止代码中进行替换时,代码结果可能出现错误

当我们希望计算结果是一个整体时,建议整体给stuff加括号

例如:#define SQUARE(x)   ((x)*(x))这样就不会出现错误了

同时,宏的参数也可以是多个

例子:

#define MAX(x,y) ((x)>(y)?(x):(y))int main()
{int m = MAX(100, 443);printf("m=%d\n", m);return 0;
}

1.3#define的替换规则

虽然我们前面说define时可能已经说过了它的替换规则,但是在这里还是要给大家总结一下,方便大家总结:

在程序中扩展#define定义符号和宏时,需要涉及几个步骤:

1.在调用宏时,首先对参数进行检查,看看是否包含任何由define定义的符号,如果是,它们首先被替换

2.替换文本随后被插入到程序中原来的文本位置,对于宏,参数名被它们的值所替换

3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define 定义的符号,如果是,就重复上述处理过程

注意:

1.宏参数和#define定义中可以出现其他#define定义的符号,但对于宏,不能出现递归

2.当预处理器搜索到#define定义的符号的时候,字符串常量的内容并不被搜索

2.#和##

1.#

 1.#它把参数插入到字符串中

2.用#把一个宏参数变为对应的字符串

这样说可能难以理解,我们直接看代码例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>#define PRINT1(x) printf("the value of "#x" is %d\n",x)#define PRINT2(format,x) printf("the value of "#x" is "format" \n",x)int main()
{int a = 10;int b = 20;float f = 3.14f;
//#把宏参数插入到字符串中PRINT1(a);PRINT1(b);//把宏参数变成对应的字符串PRINT2("%f", f);return 0;
}

2.##

1.##可以把位于它两边的符号合成一个符号

2.它允许宏定义从分离的文件片段创建标识符

但是我们要注意,这样的连接必须产生一个合法的标识符,否则其结果就是未定义的

看个代码例子:

#define CAT(x,y,z) x##y##zint main()
{int iampig = 2023;printf("%d\n", CAT(i,am,pig));return 0;
}

3.带副作用的宏参数

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

#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{int a = 4;int b = 6;int m = MAX(a++, b++);//带有副作用的参数printf("a=%d  b=%d  m=%d \n", a, b,m);return 0;
}

我们本以为打印出来的会是5,7,6.为什么会这样呢?我们来分析一下

但如果我们把它封装成函数,它就不会有这个副作用,这是因为函数会先计算再传参,和宏不一样,只替换不计算

我们看个函数例子:

int Max(int x, int y)
{return (x > y ? x : y);
}
int main()
{int a = 4;int b = 6;int m = Max(a++, b++);printf("a=%d  b=%d  m=%d \n", a, b,m);return 0;
}

所以,接下来就引出了函数和宏的对比,让我们接着学习

4.宏和函数的比较

我们从以下七点进行比较:

1.代码长度

宏:每次使用时,宏代码都会被插入到程序中,除了非常小的宏之外,程序的长度会大幅度增长

函数:函数代码只出现于一个地方,每次使用这个函数时,都会调用那个地方的同一份代码

2.执行速度
宏:更快

函数:存在函数的调用和返回的额外开销,所以相对慢一些

3.操作符的优先级

宏:宏参数的求值是在所以周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多加括号

函数:函数参数只在函数调用的时候求值一次,它的结果值传递给函数,表达式的求值结果更容易预判

4.带有副作用的参数

宏:参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果

函数:函数参数只在传参的时候求值一次,结果更容易控制

5.参数类型

宏:宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用任何参数类型

函数:函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使它们执行的任务是相同的

6.调试

宏:宏是不方便调试的

函数:函数是可以逐语句调试的

7.递归

宏:不能递归

函数:可以递归

5.命名的约定

一般来讲,函数和宏的使用语法很相似,所以C语言没有规定它们的使用语法

我们约定俗成

宏命名就大写,函数名不要全大写

但有时宏也有小写特例,我们要注意

6.#undef:可以移除宏定义

这个函数是可以移除宏定义的,我们看个例子:


#define MAX 100int main()
{printf("%d\n",MAX);//可以执行
#undef MAX//printf("%d\n",MAX);//不可执行return 0;
}

当我们#undef MAX后,在打印MXA,VS会提示我们MXA未定义,这就是它移除了宏定义。


好了,关于程序环境和预处理方面的知识,我们已经全部学完了,希望大家可以理解,关于C语言的理论方面的知识点到这里也是已经给大家全部说完了,对C语言说声拜拜。

我们下期再见!!!

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

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

相关文章

git查看某个commit属于哪个分支方法(如何查看commit属于哪个分支)

有时候&#xff0c;当我们由于业务需求很多时&#xff0c;基于同一个分支新建的项目分支也会很多。 在某个时间节点&#xff0c;我们需要合并部分功能点时&#xff0c;我们会忘了这个分支是否已经合入哪个功能点&#xff0c;我们就会查看所有的commit记录&#xff0c;当我们找到…

1.4 8位加法器

1.半加器 2.全加器 半加器: 完整模拟1位加法 1.A,B 接受端,接受1或0 , 2个电信号 2.异或门 做为结果: 1^10, 0^00, 1^01, 0^11 与编程中的: 异或一致 3.与门 做为进位: 1&11,1&00,0&10, 0&01 与编程中的: 与一致 4.半加器实现1位的加法运算,比如:A端: …

[Java]线程详解

Java线程 一、线程介绍 程序 是为完成特定任务、用某种语言编写的一组指令的集合&#xff08;简单来说就是写的代码&#xff09;。 进程 进程是指运行中的程序&#xff0c;比如我们使用的QQ&#xff0c;就启动了一个进程&#xff0c;操作系统会对该进程分配内存空间。当我…

QXDM Filter使用指南

QXDM Filter使用指南 1. QXDM简介2 如何制作和导入Filter2.1 制作Filter2.1.1 制作Windows环境下Filter2.1.2 制作Linux环境下Filter 2.2 Windows环境下导入Filter 3 Filter配置3.1 注册拨号问题3.1.1 LOG Packets(OTA)3.1.2 LOG Packets3.1.3 Event Reports3.1.4 Message Pack…

Vue3 封装组件库并发布到npm仓库

一、创建 Vue3 TS Vite 项目 输入项目名称&#xff0c;并依次选择需要安装的依赖项 npm create vuelatest 项目目录结构截图如下&#xff1a; 二、编写组件代码、配置项和本地打包测试组件 在项目根目录新建 package 文件夹用于存放组件 &#xff08;以customVideo为例&a…

系列十六、Spring IOC容器的扩展点

一、概述 Spring IOC容器的扩展点是指在IOC加载的过程中&#xff0c;如何对即将要创建的bean进行扩展。 二、扩展点 2.1、BeanDefinitionRegistryPostProcessor 2.1.1、概述 BeanDefinitionRegistryPostProcessor是bean定义的后置处理器&#xff0c;在BeanDefinition加载后&a…

C++知识点总结(7):玩转高精度除法

一、复习高低精度 一个数分为两种类型&#xff1a; 1. 高精度数&#xff0c;即一个长度特别长的数&#xff0c;使用 long long 也无法存储的一类数字。 2. 低精度数&#xff0c;即一个普通的数&#xff0c;可以使用 long long 来存储。 由于高精度除法比较简单&#xff0c;…

卸载11.3的cuda,安装11.8的cuda及cudnn

linux查看cudnn版本_linux查看cudnn版本命令_在学习的王哈哈的博客-CSDN博客文章浏览阅读2.9k次&#xff0c;点赞6次&#xff0c;收藏6次。英伟达官方文档查看cuda版本cat /usr/local/cuda/version.txt或者nvcc --version 或者 nvcc -V查看cudnn版本网上都是这个但是不行cat /u…

【超强笔记软件】Obsidian实现免费无限流量无套路云同步

【超强笔记软件】Obsidian如何实现免费无限流量无套路云同步&#xff1f; 目录 一、简介 软件特色演示&#xff1a; 二、使用免费群晖虚拟机搭建群晖Synology Drive服务&#xff0c;实现局域网同步 1 安装并设置Synology Drive套件 2 局域网内同步文件测试 三、内网穿透群…

Stable-Diffusion——Windows部署教程

Windows 参考文章&#xff1a;从零开始&#xff0c;手把手教你本地部署Stable Diffusion Webui AI绘画(非最新版) 一键脚本安装 默认环境安装在项目路径的venv下 conda create -n df_env python3.10安装pytorch&#xff1a;&#xff08;正常用国内网就行&#xff09; python -…

【Python篇】详细讲解正则表达式

文章目录 &#x1f339;什么是正则表达式&#x1f354;语法字符类别重复次数组合模式 ✨例子 &#x1f339;什么是正则表达式 正则表达式&#xff08;Regular Expression&#xff09;&#xff0c;简称为正则或正则表达式&#xff0c;是一种用于匹配、查找和操作文本字符串的工…

Linux:docker基础操作(3)

docker的介绍 Linux&#xff1a;Docker的介绍&#xff08;1&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/134146721?spm1001.2014.3001.5502 通过yum安装docker Linux&#xff1a;Docker-yum安装&#xff08;2&#xff09;-CSDN博客https://blog.…

2 时间序列预测入门:GRU

0 论文地址 GRU 原论文&#xff1a;https://arxiv.org/pdf/1406.1078v3.pdf GRU&#xff08;Gate Recurrent Unit&#xff09;是循环神经网络&#xff08;RNN&#xff09;的一种&#xff0c;可以解决RNN中不能长期记忆和反向传播中的梯度等问题&#xff0c;与LSTM的作用类似&a…

图论——二部图及其算法

什么是二部图 二部图的判定 例子1 任选一个节点染成红色 红色的邻居染成蓝色 蓝色邻居染成红色 例子2 这个不是二部图 无权二部图的最大匹配

leetcode做题笔记1457. 二叉树中的伪回文路径

给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中&#xff0c;存在一个回文序列。 请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。 示例 1&#xff1a; 输…

【计算机网络笔记】多路访问控制(MAC)协议——随机访问MAC协议

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

Error PostCSS plugin autoprefixer requires PostCSS 8

文章目录 一、情况一二、情况二三、总结 在启动 vue项目时&#xff0c;突然控制台报错&#xff1a; Error: PostCSS plugin autoprefixer requires PostCSS 8。然后依次出现下面几种情况&#xff0c;依次解决完&#xff0c;项目就可以正常启动了 一、情况一 error in ./src/…

万宾科技可燃气体监测仪的功能有哪些?

随着城市人口的持续增长和智慧城市不断发展&#xff0c;燃气作为一种重要的能源供应方式&#xff0c;已经广泛地应用于居民生活和工业生产的各个领域。然而燃气泄漏和安全事故的风险也随之增加&#xff0c;对城市的安全和社会的稳定构成了潜在的威胁。我国燃气管道安全事故的频…

redhat9.3配置国内yum阿里源

由于新建的Redhat9.3在未注册激活之前是没有yum源的配置文件的&#xff0c;所以需要我们自己新建一个yum源文件的配置文件 vim /etc/yum.repos.d/aliyun_yum.repo 内容如下&#xff1a; [ali_baseos] nameali_baseos baseurlhttps://mirrors.aliyun.com/centos-stream/9-str…

基于Flutter的图片浏览器的实现

一 效果展示&#xff1a; 1. 图片展示&#xff1a; 2.混色&#xff0c;平铺&#xff0c;拉伸&#xff0c;原图展示 二 实验准备&#xff1a; 1.在包结构中创建images包来存放我们用到的图片&#xff0c;在pubspec.yaml中声明路径&#xff1a; 2. 检查虚拟机是否正常运行&…