【C语言】预处理常见知识详解(宏详解)

文章目录

  • 1、预定义符号
  • 2、define
    • 2.1 define 定义常量
    • 2.2 define 定义宏
  • 3、#和##
    • 3.1 **#**
    • 3.2 **##**
  • 4、条件编译(开关)


1、预定义符号

在C语言中内置了一些预定义符号,可以直接使用,这些符号实在预处理期间处理的,并且这些符号都是C语言ANSIC里收集的

在这里插入图片描述

但是在笔者的VS2022里不完全支持ANSIC的所有预定义符号

在这里插入图片描述

这里把这里的预定义处理STDC都打印了一遍,证明都可以实现,但是当我们打印STDC时会出现
在这里插入图片描述
由此可见随着编译器版本的升级有些C语言内置预定义会被忽视掉

2、define

2.1 define 定义常量

基本语法

#define name stuff

以下举例

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

思考:需要在定义后加上“;”吗?
例如:

#define MAX 1000
#define MAX 1000;

建议是不加上,可能会导致原来语句顺序发生错乱
假如上上面的MAX 1000最后加上了一个分号就会导致以下的运行情况

if(condition)max = MAX;;
elsemaX = 0

这种情况下,相当于if后面跟了两条语句,但是如果没有大括号的情况下,是不允许有多条语句的,所以这会导致if语句加执行语句max = MAX;,后跟了一条空语句,这会导致else前面断开,产生语法错误。

2.2 define 定义宏

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

申明方式

#define name( parament-list ) stuff

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

  • 注意1:parament-list 的左括号必须紧贴在name的右边,一旦有空格在中间,就会让宏判定 ( parament-list ) 属于后面的 stuff

  • 注意2:宏一旦运行后会直接替换 parament-list 的内容到stuff里, stuff 及 parament-list 里的计算符的优先级就可能会导致计算顺序发生变化,
    所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

例如,现在有这样一个宏

#define double(x) x*x

没有注意用括号区分优先级的话,当 x 为 x+1 时,
这个宏的运算的顺序就是 x+1*x+1,即 x + x +1,最终导致结果发生较大偏差
但如果我们定义宏为

#define double(x) (x)*(x)

就不会在导致这种情况

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

以下举例

#define MAX(X,Y) ((X)>(Y)?(X):(Y))int main()
{int a = 3;int b = 5;int m = MAX(a++, b++);//相当于替换成//int m = MAX(a++,b++) ((a++)>(b++)?(a++):(b++))//a++和b++各会执行两次printf("m = %d\n", m);//6printf("a = %d\n", a);//4printf("b = %d\n", b);//7return 0;
}

宏替换规则总结
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

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

注意

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

宏和函数的对比
在这里插入图片描述

命名规则
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者那我们平时的一个习惯是:

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

其中内置宏 offsetof 比较特殊,它是红,但是全部小写,它的作用是计算结构体成员相对结构体起始位置的偏移量

#undef
用来移除一个宏定义

例如下图
在这里插入图片描述
原来没有移除 MAX 的时候,是可以打印出来的,但是一移除后就显示未定义标识符。


3、#和##

3.1 #

#运算符将宏的一个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。
#运算符所执行的操作可以理解为”字符串化“。

例如:当我们有一个变量 int a = 10;的时候,我们想打印出:the value of ais 10
就可以写下面这个宏

#define PRINT(n) printf("the value of "#n " is %d", n);

当我们把a替换到宏的体内时,就出现了#a,而#a就是转换为"a”,时一个字符串代码就会被预处理为:

printf("the value of ""a" " is %d", a);

结果就是

the value of a is 10

3.2 ##

##可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。 ## 被称为记号粘合
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。
这里我们想想,写一个函数求2个数的较大值的时候,不同的数据类型就得写不同的函数。

比如,我们相要写下面这两个函数,但是要不断地写他们的类型名,这里我们可以利用宏

int int_max(int x, int y)
{return x > y ? x : y;
}
float float_max(float x, float y)
{return x > y ? x:y;
}

我们相要写上面这两个函数,但是要不断地写他们的类型名,这样太繁琐了,这里我们可以利用宏

//宏定义
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
return (x>y?x:y); \
}

其中 ## 的作用是链接type和后面的_max,否则,当type被输入后,type_max 就会被认为时一个,使得函数名一直是 type_max

定义好宏后,我们放入类型名进去,就会产生新的函数名,然后可以进行使用

GENERIC_MAX(int) //替换到宏体内后int##_max ⽣成了新的符号 int_max做函数名
GENERIC_MAX(float) //替换到宏体内后float##_max ⽣成了新的符号 float_max做函数名int main()
{
//调⽤函数
int m = int_max(2, 3);
printf("%d\n", m);
float fm = float_max(3.5f, 4.5f);
printf("%f\n", fm);
return 0;
}

最终结果是

3
4.500000

4、条件编译(开关)

当我们在调试时需要写一些语句辅助,但是正式发布时可能是不带的,我们一遍在正式使用时会注释掉,但这仅适用于小型代码,一旦代码量达到一个恐怖速度,就可能会有缺漏,并造成巨大的工作量,所以就用到了部分条件编译,用作开关

例如

#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;
}

这里的代码,中间那条输出就是调试时观察用的,我们在开头定义宏,一旦不需要用掉,只需要把宏去了,就不会运行这行代码,这种方法用在代码很长的时候很有用,用做开关。

除了这个 #ifdef 还有很多别的

1.
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif2.多个分⽀的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol4.嵌套指令
#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

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

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

相关文章

安卓国内ip代理app,畅游网络

随着移动互联网的普及和快速发展&#xff0c;安卓手机已经成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;由于地理位置、网络限制或其他因素&#xff0c;我们有时需要改变或隐藏自己的IP地址。这时&#xff0c;安卓国内IP代理App便成为了一个重要的工具。虎观代理…

局域网找不到共享电脑怎么办?

局域网找不到共享电脑是一种常见的问题&#xff0c;给我们的共享与合作带来一定的困扰。天联组网技术可以解决这个问题。本文将介绍天联组网的原理和优势&#xff0c;并探讨其在解决局域网找不到共享电脑问题中的应用。 天联组网的原理和优势 天联组网是一种基于加速服务器的远…

ES6 学习(三)-- es特性

文章目录 1. Symbol1.1 使用Symbol 作为对象属性名1.2 使用Symbol 作为常量 2. Iterator 迭代器2.1 for...of循环2.2 原生默认具备Interator 接口的对象2.3 给对象添加Iterator 迭代器2.4 ... 解构赋值 3. Set 结构3.1 初识 Set3.2 Set 实例属性和方法3.3 遍历3.4 相关面试题 4…

2024年天府杯A题论文免费分享,全网首发

天府杯免费分享资料&#xff08;A题论文代码&#xff09;链接&#xff1a;https://pan.baidu.com/s/17QtYt036ORk1xGIDi0JSew 提取码&#xff1a;sxjm 摘要 在近年来&#xff0c;随着科技的快速发展和社会经济的不断进步&#xff0c;科学研究的作用和地位日益凸显。本文基于…

springdata框架对es集成

什么是spring data框架 Spring Data是一个用于简化数据库、非关系型数据库、索引库访问&#xff0c;并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷&#xff0c;并支持 map-reduce框架和云计算数据服务。Spring Data可以极大的简化JPA(Elasticsearch…)的…

语音模块摄像头模块阿里云结合,实现垃圾的智能识别

语音模块&摄像头模块&阿里云结合 文章目录 语音模块&摄像头模块&阿里云结合1、实现的功能2、配置2.1 软件环境2.2 硬件配置 3、程序介绍3.1 程序概况3.2 语言模块SDK配置介绍3.3 程序文件3.3.1 开启摄像头的程序3.3.2 云端识别函数( Py > C ) & 串口程序…

Intellij IDEA安装配置Spark与运行

目录 Scala配置教程 配置Spark运行环境 编写Spark程序 1、包和导入 2、定义对象 3、主函数 4、创建Spark配置和上下文 5、定义输入文件路径 6、单词计数逻辑 7、输出结果 8、完整代码&#xff1a; Scala配置教程 IDEA配置Scala&#xff1a;教程 配置Spark运行环境 …

【微服务】Nacos(配置中心)

文章目录 1.AP和CP1.基本介绍2.说明 2.Nacos配置中心实例1.架构图2.在Nacos Server加入配置1.配置列表&#xff0c;加号2.加入配置3.点击发布&#xff0c;然后返回4.还可以编辑 3. 创建 Nacos 配置客户端模块获取配置中心信息1.创建子模块 e-commerce-nacos-config-client50002…

web学习笔记(四十九)

目录 1. 初识 Express 1.1 什么是 Express 1.2 Express 能做什么 1.3 Express 的基本使用 1.4 如何把内容响应给客户端 2. 托管静态资源 2.1 express.static() 1. 初识 Express 1.1 什么是 Express 之前我们有讲过可以通过node.js内置的http模块来创建服务器&#x…

需求生命周期管理

背景 回顾很多项目或者产品&#xff0c;我们发现现在的版本和当初的理解或者设想是天壤之别&#xff0c;这是什么原因&#xff0c;对于这种情况又应该如何处理呢&#xff1f; 业务分析的交付物是需求文档&#xff0c;业务分析整个过程随着对业务的逐步深入&#xff0c;观察视…

【3DsMax+Pt】练习案例

目录 一、在3DsMax中展UV 二、在Substance 3D Painter中绘制贴图 一、在3DsMax中展UV 1. 首先创建如下模型 2. 选中如下三条边线作为接缝 重置剥 发现如下部分还没有展开 再选一条边作为接缝 再次拨开 拨开后的UV如下 二、在Substance 3D Painter中绘制贴图 1. 新建项目&am…

JVM之EhCache缓存

EhCache缓存 一、EhCache介绍 在查询数据的时候&#xff0c;数据大多来自数据库&#xff0c;咱们会基于SQL语句的方式与数据库交互&#xff0c;数据库一般会基于本地磁盘IO的形式将数据读取到内存&#xff0c;返回给Java服务端&#xff0c;Java服务端再将数据响应给客户端&am…

微服务(基础篇-004-Feign)

目录 http客户端Feign Feign替代RestTemplate&#xff08;1&#xff09; Feign的介绍&#xff08;1.1&#xff09; 使用Feign的步骤&#xff08;1.2&#xff09; 自定义配置&#xff08;2&#xff09; 配置Feign日志的两种方式&#xff08;2.1&#xff09; Feign使用优化…

一只英短,有什么牌子的口粮适合它?

亲爱的铲屎官&#xff0c;你问到了一只英短适合什么牌子的口粮&#xff0c;那我一定要推荐你试试福派斯鲜肉无谷猫粮&#xff01;&#x1f431;&#x1f35a; 首先&#xff0c;英短猫咪的肠胃比较敏感&#xff0c;所以选择一款无谷的猫粮是非常重要的。福派斯鲜肉无谷猫粮就是一…

Python人工智能:气象数据可视化的新工具

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能&#xff0c;这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…

SpringBoot实现RabbitMQ的广播交换机(SpringAMQP 实现Fanout广播交换机)

文章目录 声明交换机消息接收消息发送效果 广播交换机的特定是: 将消息交给所有绑定到交换机的队列 在广播模式下&#xff0c;消息发送流程是这样的&#xff1a; 1&#xff09; 可以有多个队列2&#xff09; 每个队列都要绑定到 Exchange&#xff08;交换机&#xff09;3&…

信息安全之网络安全防护

先来看看计算机网络通信面临的威胁&#xff1a; 截获——从网络上窃听他人的通信内容中断——有意中断他人在网络上的通信篡改——故意篡改网络上传送的报文伪造——伪造信息在网络上传送 截获信息的攻击称为被动攻击&#xff0c;而更改信息和拒绝用户使用资源的攻击称为主动…

RabbitMQ安装及使用笔记

RabbitMQ安装及使用笔记 RabbitMQ是一个开源的消息代理软件&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c;用于在分布式系统中进行消息传递。 1.安装 利用docker load命令加载mq镜像 docker load -i mq.tar 基于Docker来安装RabbitMQ&#xff…

【计算机网络】http协议的原理与应用,https是如何保证安全传输的

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

C# 高级文件操作与异步编程探索(初步)

文章目录 文本文件的读写探秘StreamReader 类深度剖析StreamWriter 类细节解读编码和中文乱码的解决方案 二进制文件的读写BinaryReader 类全面解析BinaryWriter 类深度探讨 异步编程与C#的未来方向同步与异步&#xff1a;本质解读Task 的神奇所在async/await 的魔法 在现代编程…