当前位置: 首页 > news >正文

【C语言】宏

在这里插入图片描述

🚀write in front🚀
📜所属专栏:> c语言学习
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!

在这里插入图片描述

文章目录

  • 前言:
  • 一.宏:
    • #define:
        • ①. #define 定义标识符:
        • ②.#define 定义宏:
        • ③. #define 替换规则:
        • ④#与##的使用:
          • #的使用:
          • ##的使用:
  • 二.宏与函数对比:
  • 三. 宏与函数的命名约定:
  • 四.#undef:
  • 五.条件编译:
    • 常见的条件编译指令:
      • ①. 单分支条件编译指令:
      • ②. 多分枝条件编译指令:
      • ③. 判断是否被定义:
      • ④.嵌套指令:
  • 六.文件包含
    • 1.头文件被包含的方式:
    • 2.嵌套文件包含
  • 总结:

前言:

  在上一篇博客中,我们学习了程序环境和预处理相关的知识。我们在预处理阶段就是在对宏进行替换,今天我就来给大家详细讲解于宏相关的知识!

一.宏:

#define:

  #define 的用处非常多,就比如我们常用的定义标识符常量、定义宏等等,而在这个过程中,也有一些细节值得我们去注意。

①. #define 定义标识符:

我们常常会使用 #define 去定义一些标识符来方便我们的代码书写:

语法:#define name stuff

定义了之后,只有我们看到 name 这个东西,就给他一比一替换成 stuff这个东西就行即可

举个栗子:

#define MAX 1000
#define reg register
//为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)
//用更形象的符号来替换一种实现
#define CASE break;case
//在写case语句的时候自动把 break写上。// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \date:%s\ttime:%s\n",\__FILE__,__LINE__,\__DATE__,__TIME__) 

注意:
  在 #define 的最后,最好不要加上分号 “ ; ” 加上可能会出现两个;号,可能会导致语法错误:

②.#define 定义宏:

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

宏的申明方式为:

#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号列表,且可能出现在 stuff 中

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

易错点:

初学者在学习宏时很容易出现以下问题:
举个栗子:

#define SQUARE( x ) x * x
int main
{
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
}

乍一看,你可能觉得这段代码将打印36这个值。
事实上,它将打印11.
为什么?

我们前面说过了,标识符的替换是一比一替换
替换文本时,参数x被替换成a + 1,所以这条语句实际上变成了:
printf (“%d\n”,a + 1 * a + 1 ),所以结果是11。

所以我们可以对这个宏进行修改:

#define SQUARE(x) (x) * (x)

由此可见,我们在进行宏定义的时候,一定要舍得加括号避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

③. #define 替换规则:

#define 在进行符号替换时,遵循以下规则

  1. 调用宏时首先对参数进行检查,检查是否包含任何由 #define 定义的符号。如果有,它们将首先被替换
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果有,就重复上述处理过程。

注意:
4. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
5. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

④#与##的使用:

#的使用:

  上面我们说到字符串常量的内容不会被#define定义的情况下替换掉。那么,我们如何将参数插入字符串中?

首先我们看看这样的代码:

char* p = "hello ""bit\n";
printf("hello"" bit\n");
printf("%s", p);

这里输出的是不是
hello bit ?
答案是确定的:是。
我们发现字符串是有自动连接的特点的

方法一:
我们可以将带有" "(双引号)的字符串替换掉原字符串中

举个栗子:

#define PRINT(FORMAT, VALUE)\printf("the value is "FORMAT"\n", VALUE);
...
PRINT("%d", 10)

这里的FORMAT替换为"%d",正好完成拼接在这里我们就可以正常打印出结果了!这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中。
方法二:
对于没有" "(双引号)的标识符怎么替换字符串中的标识符呢?
其实我们只要在要替换的标识符前加一个#即可,此时就把一个宏参数变成对应的字符串

举个栗子:

int i = 10;
#define PRINT(FORMAT, VALUE)\printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
PRINT("%d", i+3);

代码中的 #VALUE 会预处理器处理为:
"VALUE"
最终的输出的结果应该是

the value of i+3 is 13
##的使用:

##可以把位于它两边的符号合成一个符号
它允许宏定义从分离的文本片段创建标识符。

举个栗子:

#define ADD_TO_SUM(num, value) \sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10

注:
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

二.宏与函数对比:

宏通常被应用于执行简单的运算。
比如在两个数中找出较大的一个。

#define MAX(a, b) ((a)>(b)?(a):(b))

为什么不用函数来完成这个任务
原因有二

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
    所以宏比函数在程序的规模和速度方面更胜一筹
  2. 更为重要的是函数的参数必须声明为特定的类型。
    所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。
    宏是类型无关的。

当然宏和函数相比宏也有劣势的地方,

原因如下:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度
  2. 宏是没法调试的
  3. 宏由于类型无关,也就不够严谨
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错(记得加括号)

当然,宏也可以做到函数做不到的事情,比如:宏的参数可以出现类型,但是函数做不到

、#define MALLOC(num, type)\(type *)malloc(num * sizeof(type))
...
//使用
MALLOC(10, int);//类型作为参数//预处理器替换之后:
(int *)malloc(10 * sizeof(int));

三. 宏与函数的命名约定:

一般来讲,我们使用的宏定义函数,与普通函数的使用语法很相似,这就导致语言本身无法帮我们区分二者。所以我们通常对二者的命名进行约定:

宏名全部大写
函数名不要全部大写

四.#undef:

这条指令用于移除一个宏定义。

#undef NAME
如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除

五.条件编译:

  通过使用条件编译指令,我们在编译一段代码时如果要将一条(或一组)语句编译或舍弃是很方便的。例如一些调试性的代码,删除可惜,保留碍事,于是我们就可以通过使用条件编译指令来实现选择性的进行编译。

举个栗子:

#include <stdio.h>
#define __DEBUG__
int main()
{int i = 0;int arr[10] = {0};for(i=0; i<10; i++){arr[i] = i;#ifdef __DEBUG__printf("%d\n", arr[i]);//为了观察数组是否赋值成功。 #endif //__DEBUG__}return 0;
}

当我们发现赋值成功的时候,只要把#define __DEBUG__这句代码删掉就行。

常见的条件编译指令:

①. 单分支条件编译指令:

#if 常量表达式//...
#endif
//常量表达式由预处理器求值。如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif

②. 多分枝条件编译指令:

2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif

③. 判断是否被定义:

3.判断是否被定义
#if defined(symbol)
等价于:#ifdef symbol#if !defined(symbol)
等价于:#ifndef symbol

④.嵌套指令:

#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif

六.文件包含

1.头文件被包含的方式:

  • 本地文件包含
#include "filename"

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在找
准位置查找头文件。
如果找不到就提示编译错误。

  • 库文件包含
#include <filename.h>

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
这样是不是可以说,对于库文件也可以使用 “” 的形式包含?

答案是肯定的,可以,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

2.嵌套文件包含

如果出现这样的场景:
在这里插入图片描述
我们会发现:最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。当程序越复杂,程序就和同时打开很多一模一样的文件。导致预处理之后的代码很冗长。

如何解决这个问题?
答案:条件编译。
每个头文件的开头写:

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   //__TEST_H__

或者:

#pragma once

总结:

  c语言的相关知识到这里就结束了!之后我会出一个总结的思维导图!更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
c语言学习
算法
智力题
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

在这里插入图片描述

相关文章:

【C语言】宏

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;> c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是…...

【测试面试】自我分析+功能+接口自动化+性能测试面试题(大全),知己知彼百战百胜......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 分析自己和面试企业…...

ASE4N65SE-ASEMI高压MOS管ASE4N65SE

编辑-Z ASE4N65SE在TO-220F封装里的静态漏极源导通电阻&#xff08;RDS(ON)&#xff09;为2.5Ω&#xff0c;是一款N沟道高压MOS管。ASE4N65SE的最大脉冲正向电流ISM为16A&#xff0c;零栅极电压漏极电流(IDSS)为10uA&#xff0c;其工作时耐温度范围为-55~150摄氏度。ASE4N65S…...

MyBatis概述环境搭建(一)

&#x1f697;MyBatis学习起始站~ &#x1f6a9;本文已收录至专栏&#xff1a;数据库学习之旅 &#x1f44d;希望您能有所收获 一.什么是MyBatis (1) 引言 MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDB…...

3.8国际妇女节即将到来,跨境卖家如何做好选品和营销?

不知不觉&#xff0c;时间已来到了2月末&#xff0c;一年一度的三八国际妇女节也即将来临。三八节又称女神节&#xff0c;这不仅是庆祝女性伟大贡献的日子&#xff0c;也是跨境卖家们促销的大好时机。 有数据显示&#xff0c;女性是跨境消费的主力人群&#xff0c;占比超七成&…...

Glue Connector 和 Connection 的关系与区别

AWS Glue作为一种无服务器产品&#xff0c;其运行环境是“不可预知”的&#xff0c;也就是“一个黑盒”&#xff0c;所以如何能连接一些自有数据源是Glue必须考虑并给予满足的&#xff0c;为此&#xff0c;Glue给出的解决方案就是Connector和Connection&#xff0c;一个connect…...

如何使用ngxin的 upstream

1.引言&#xff1a; 1.1反向代理&#xff1a; 反向代理是充当Web服务器网关的代理服务器。当您将请求发送到使用反向代理的Web服务器时&#xff0c;他们将先转到反向代理&#xff0c;由该代理将确定是将其路由到Web服务器还是将其阻止。 这意味着有了反向代理&#xff0c;您…...

Java数组,超详细整理,适合新手入门

目录 一、什么是Java中的数组&#xff1f; 二、数组有哪些常见的操作&#xff1f; 三、数组的五种赋值方法和使用方法 声明数组 声明数组并且分配空间 声明数组同时赋值(1) 声明数组同时赋值(2) 从控制台输入向数组赋值 四、求总和平均 五、求数组中最大值最小值 六…...

1.3数据传输控制方式:IO数据传输控制方式、程序控制(查询)方式、程序中断方式、DMA方式、通道方式、I/O处理机

1.3数据传输控制方式&#xff1a;IO数据传输控制方式、程序控制&#xff08;查询&#xff09;方式、程序中断方式、DMA方式、通道方式、I/O处理机程序控制&#xff08;查询&#xff09;方式程序中断方式DMA方式通道方式、I/O处理机I/O数据传输方式&#xff0c;由软件到硬件发展…...

Linux 设置语言

文章目录1. 临时设置环境变量2. 默认语言设置3. 语言包4. 安装浏览器 chromium1. 临时设置环境变量 通过设置环境变量&#xff0c;可以使单个命令使用另一种语言LANG $ LANGfr_FR.utf8 date mar. mai 24 12:16:51 CDT 2022后续命令将恢复为使用系统的默认语言进行输出。该loc…...

Python基础-数据类型之集合

一、集合的定义 集合&#xff1a;是一个无序的没有重复元素的序列&#xff0c;因此不能通过索引来进行操作 1&#xff1a;使用set()创建集合 set(object) # 参数为一个序列&#xff0c;整型不能作为参数 set_a set("abcb") print(set_a) # {b, a, c} 2&…...

[Css]Grid属性简单陈列(适合开发时有基础的快速过一眼)

[css进阶]Grid属性简介 文章目录[css进阶]Grid属性简介典型需求网格容器的属性displaygrid-template-columns和grid-template-rowsgrid-template-areasgrid-templategrid-column-gap grid-row-gapgrid-gapjustify-itemsalign-itemsjustify-contentalign-contentgrid-auto-colum…...

100种思维模型之启发式偏差思维模型-017

曾国藩在给儿子的一封家书中曾写道&#xff1a;余于凡事皆用困知勉行工夫&#xff0c;尔不可求名太骤&#xff0c;求效太捷也。熬过此关&#xff0c;便可少进。再进再困&#xff0c;再熬再奋&#xff0c;自有亨通精进之日。 不急躁不求捷径&#xff0c;小火慢炖&#xff0c;将事…...

微服务 feign远程调用时 显示服务不可用 timed-out and no fallback

目录 第一种: failed and no fallback available 1 服务挂掉了 2 服务没有开启 3 注册中心没注册进去 -> ps: 直接调用的接口 通过网关转发失败 会报503 4 高并发下的服务熔断了 第二种: timed-out and no fallback 2.1 业务场景: A服务一切正常 但是B服务显示timeo…...

第一个Java程序(初识Java)

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【JavaSE_primary】 文章目录1.Java概述1.1什么是Java1.2Java之父2.0第一个Java程序编译运行.class3.0程序如何跑起来的&#xff1f;3.1J…...

vulnhub LordOfTheRoot_1.0.1

总结&#xff1a;端口敲门&#xff0c;CVE-2015-8660提权&#xff0c; 目录 下载地址 漏洞分析 信息收集 端口敲门 网站分析 方法一 ssh登录提权 方法二 下载地址 LordOfTheRoot_1.0.1.ova (Size: 1.6 GB)Download: http://www.mediafire.com/download/m5tbx0dua05szjm…...

MutationObserver与IntersectionObserver

MutationObserver 出现原因&#xff1a;当我们需要监听元素发生变化时&#xff0c;不借助使元素发生变化的业务动作的情况下&#xff0c;使用无污染方式监听非常困难&#xff0c;为了解决这个问题&#xff0c;MutationObserver诞生&#xff01; 概述 可以用来监听DOM的任何变化…...

【ESP 保姆级教程】玩转巴法云篇② ——MQTT设备云,MQTT协议下的数据通信

忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-02-21 ❤️❤️ 本篇更新记录 2023-02-21 ❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请…...

植物大战 仿函数——C++

容器适配器 容器适配器不支持迭代器。栈这个东西&#xff0c;让你随便去遍历&#xff0c;是不好的。他是遵循后进先出的。所以他提供了一个街头top取得栈顶数据。 仿函数 仿函数&#xff08;functor&#xff09;是C中一种重载了函数调用运算符&#xff08;operator()&#x…...

【C语言】浮点型数据在内存中的存储

&#x1f680;&#x1f680;&#x1f680; 如果文章对你有帮助不要忘记点赞关注收藏哦&#x1f680;&#x1f680;&#x1f680; 文章目录⭐浮点数在内存中的存储1.1 &#x1f913;举个例子:1.2浮点数存储规则&#x1f308;&#xff1a;对于M与E有一些特别规定1.3解释前面题目&…...

impala中的刷新元数据和刷新表

impala是Cloudera公司主导开发的新型查询系统&#xff0c;它提供SQL语义&#xff0c;能查询存储在Hadoop的HDFS和HBase中的PB级大数据。 虽然Hive系统也提供了SQL语义&#xff0c;但由于Hive底层执行使用的是MapReduce引擎&#xff0c;仍然是一个批处理过程&#xff0c;难以满…...

Vscode创建vue项目的详细步骤

目录 一、概述 操作的前提 二、操作步骤 一、概述 后端人员想在IDEA里面创建一个Vue的项目&#xff0c;但是这非常麻烦&#xff0c;用vscode这个前端专用软件创建就会非常快速。 操作的前提 1.安装vscode软件的步骤&#xff1a;vscode下载和安装教程和配置中文插件&#…...

如何在面试中介绍自己的项目,才能让软件测试面试官无可挑剔,

四、项目 4.1 简单介绍下最近做过的项目 根据自己的项目整理完成&#xff0c;要点&#xff1a; 1&#xff09;项目背景、业务、需求、核心业务的流程 2&#xff09;项目架构&#xff0c;B/S还是C/5&#xff0c;数据库用的什么? 中间件用的什么&#xff1f;后台什么语言开发…...

虹科方案|从 uCPE 到成熟的边缘计算平台

基于开放硬件平台&#xff0c;通用客户端设备 (uCPE) 支持快速添加、集成或删除任意数量的集中管理虚拟功能。 为了增加收入并保持竞争优势&#xff0c;托管服务提供商 (MSP) 和企业正在部署 uCPE 以增强业务敏捷性、加速新服务的引入并提高运营效率。最初&#xff0c;uCPE被部…...

计算机是怎么读懂C语言的?

文章目录前言程序环境翻译环境翻译环境分类编译预处理预处理符号预定义符号#define#undef命令行定义条件编译文件包含头文件包含查找规则嵌套文件包含其他预处理指令编译阶段汇编链接&#x1f389;welcome&#x1f389; ✒️博主介绍&#xff1a;博主大一智能制造在读&#xff…...

hadoop入门介绍及各组件功能运行关系

文章目录Hadoop 组成部分1.HDFS2.MapReduce 架构概述3. yarn 架构概述4.HDFS、YARN、MapReduce三者关系Hadoop 组成部分 1.HDFS Hadoop Distributed File System&#xff0c;简称 HDFS&#xff0c;是一个分布式文件系统。 HDFS 架构概述 主要分为 NameNode (mn):存储文件的元…...

(HP)新手引导使用react-shepherd

1&#xff0c;官方参数文档&#xff1a;https://shepherdjs.dev/docs/tutorial-02-usage.html 2&#xff0c;基本代码 import { ShepherdTour } from react-shepherd; import ./index.less; // 自己的样式文件&#xff0c;用来修改样式 import ./shepherd.less; // 将shephe…...

数据结构:栈和队列(Leetcode20. 有效的括号+225. 用队列实现栈+232. 用栈实现队列)

目录 一.数据结构--栈 1.栈的基本介绍 2.栈的实现 二.数据结构--队列 1.队列的基本介绍 2.队列的实现 三.栈的运用(Leetcode20. 有效的括号225) 1.问题描述 2.问题分析 题解代码&#xff1a; 四.用两个队列实现栈(225. 用队列实现栈 - 力扣&#xff08;Leetcode&a…...

22.2.19周赛双周赛(贪心、记忆化搜索...)

文章目录双周赛98[6359. 替换一个数字后的最大差值](https://leetcode.cn/problems/maximum-difference-by-remapping-a-digit/)[6361. 修改两个元素的最小分数](https://leetcode.cn/problems/minimum-score-by-changing-two-elements/)贪心排序[6360. 最小无法得到的或值](ht…...

2023最新软件测试面试题(带答案)

1. 请自我介绍一下(需简单清楚的表述自已的基本情况&#xff0c;在这过程中要展现出自信&#xff0c;对工作有激情&#xff0c;上进&#xff0c;好学) 面试官您好&#xff0c;我叫###&#xff0c;今年26岁&#xff0c;来自江西九江&#xff0c;就读专业是电子商务&#xff0c;毕…...