【C语言】预处理详解

在这里插入图片描述

前言
在上一篇博客中,我们了解了代码是如何执行的,简单介绍了编译中预处理步骤,在这篇博客中我们将详细了解预处理。


文章目录

  • 一、预定义符号
  • 二、#define定义
    • 2.1 定义常量
    • 2.2 定义宏
    • 2.3 创建代码片段
  • 三、#和##运算符
    • 3.1 字符串化操作符#
    • 3.2 连接操作符##
  • 四、宏和函数对比
  • 五、条件编译

一、预定义符号

预定义符号通常是指编程语言或编译器提供的一组特定的符号或宏,用于在代码中执行某些特定的功能或获取某些信息。

__FILE__ //返回当前源文件的文件名
__LINE__ //返回当前代码行的行号。
__DATE__ //返回编译的日期
__TIME__ //返回编译的时间
__func__ //或 __FUNCTION__(C++):返回当前函数的名称
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义

一个使用场景

// 在错误消息中使用 __FILE__ 和 __LINE__ 打印出源代码文件名和行号
printf("Error in %s, line %d: Division by zero\n", __FILE__, __LINE__);       

二、#define定义

#define 是一个预处理指令,用于在C、C++和其他一些编程语言中创建宏或定义符号。它在编译前执行文本替换,将指定的符号替换为其对应的文本。

2.1 定义常量

//定义整数常量
#define MAX_VALUE 100//定义字符串常量
#define WELCOME_MESSAGE  "Hello, World!"

2.2 定义宏

宏是一种可以接受参数并展开为一段代码的定义。例如,你可以创建一个用于计算平方的宏。

#define SQUARE(x) ((x) * (x))int main(){int a = 5;printf("%d",SQUARE(a))://SQUARE(a)会被替换成((a) * (a)),结果是25
}

需要注意的是 \color{#FF0000}{需要注意的是} 需要注意的是 ,((x) * (x))内部不加括号会计算错误
例如:

#define SQUARE(x) (x * x)
int main(){int a = 5;printf("%d",SQUARE(a+1))://SQUARE(a)会被替换成(a+1*a+1),结果是11
}

同样的,((x) * (x))外部不加括号可能也会计算错误
举例:

#define DOUBLE(x) (x) + (x)
int main(){int a = 5;printf("%d",10*DOUBLE(a))://DOUBLE(a)会被替换成 10*(a)+(a),结果是55
}

因此,在定义宏时需格外注意。


此外, 副作用的宏参数 \color{#FF0000}{副作用的宏参数 } 副作用的宏参数 也需要格外注意

副作用的宏参数指的是在宏展开时,宏参数在宏中多次出现,或者在宏中进行了多次计算,特别是当参数包含函数调用、递增/递减操作或其他会改变参数状态的操作时,可能导致意外的行为或错误结果

例如下面这个例子

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )int main() {int x = 5;int y = 8;int z = MAX(x++, y++);printf("x=%d y=%d z=%d\n", x, y, z);//输出结果是x=6 y=10 z=9return 0;
}

在( (x++) > (y++) ? (x++) : (y++) )中进行了两次y的自增,一次的x自增,但是是后缀自增,所以在y第二次自增前返回给了z,所以z是9。


2.3 创建代码片段

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

#define PRINT_MESSAGE(msg) printf("Message: %s\n", msg)//如果定义的代码过⻓,可以分成⼏⾏写,除了最后⼀⾏外,每⾏的后⾯都加⼀个反斜杠(续⾏符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \date:%s\ttime:%s\n" ,\__FILE__,__LINE__ , \__DATE__,__TIME__ ) 

三、#和##运算符

3.1 字符串化操作符#

# 可以用于将宏参数转换为字符串,称为字符串化。

#define STRINGIZE(x) #x
int main() {int a = 5;printf("将%d转换为字符%s",a,STRINGIZE(5));return 0;
}

在这里插入图片描述


3.2 连接操作符##

## 可以用于将两个宏参数连接成一个标识符,称为连接操作符(记号粘合)。

例如:

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

这样我们就可以定义根据返回类型来命名不同的函数

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

四、宏和函数对比

宏和可以实现简单函数的功能,对于实现相对简单的功能,宏更具有优势。但宏并不能取代函数。
以下是宏和函数的对比:

-#define定义宏函数
编译时 vs. 运行时宏是在编译时执行的文本替换操作。宏展开在编译阶段,不会引入运行时开销函数是在运行时执行的,需要在每次调用时进入和退出函数,具有运行时开销
类型安全宏是文本替换,没有类型检查。它们不会执行参数类型检查,可能引入类型错误函数具有明确的参数和返回值类型,编译器会执行类型检查,可以捕获类型错误
可读性和维护性宏可以导致代码变得难以阅读,因为它们是文本替换,展开后的代码可能很复杂函数具有明确的名称,参数和返回值,有助于代码的可读性和可维护性
代码复用宏可以在多个地方重复使用,但它们通常不能返回值,难以实现复杂的逻辑函数可以在多个地方调用,并且可以返回值,可以实现更复杂的逻辑
副作用宏可以包含副作用函数内的副作用通常较少
调试调试宏时,你只能查看宏在源代码中的展开,不容易进行调试函数是独立的代码单元,可以更容易进行单步调试和查看函数的堆栈跟踪

五、条件编译

在编译⼀个程序的时候我们如果不想编译⼀条语句(⼀组语句),除了将其注释掉,还可以使用条件编译。
条件编译是一种在C和C++中根据条件选择性包含或排除代码的技术。它使用预处理器指令来控制编译过程中哪些代码块应该被编译进最终的可执行程序。

条件编译使用以下预处理器指令来实现:

  • #ifdef 和 #ifndef:检查是否已定义了某个符号。
  • #if:执行条件判断,通常与预定义符号一起使用。
  • #else:在条件不成立时执行一段代码。
  • #elif:用于多个条件的情况,类似于 else if。
  • #endif:结束条件编译块。

例如:

#include <stdio.h>
#define DEBUG
int main() {int x = 10;#ifdef DEBUG //检查是否已定义了某个符号printf("Debugging information: x = %d\n", x);
#else //在条件不成立时执行一段代码printf("Release information\n");
#endif //结束条件编译块return 0;
}
//我们已经定义了DEBUG,所以会执行语句printf("Debugging information: x = %d\n", x);

在这里插入图片描述


在这里插入图片描述
如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。

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

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

相关文章

【C++】:关键字+命名空间+输入输出+缺省参数+函数重载+引用

【本节目标】 C关键字命名空间C输入&输出缺省参数函数重载引用 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等 熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;本章节主要目标&#xff…

ROS 摄像头标定-camera_calibration

摄像头这种精密仪器对光学器件的要求较高&#xff0c;由于摄像头内部与外部的一些原因&#xff0c;生成的物体图像往往会发生畸变&#xff0c;为了避免数据源造成的误差&#xff0c;需要针对摄像头的参数进行标定。 ROS官方提供了用于双目和单目摄像头标定的功能包—camera_cal…

Hadoop分布式集群搭建教程

目录 前言环境准备一、创建虚拟机二、虚拟机网络配置三、克隆虚拟机四、Linux系统配置五、Hadoop的部署配置六、Hadoop集群的启动Bug解决参考文章 前言 大数据课程需要搭建Hadoop分布式集群&#xff0c;在这里记录一下搭建过程 环境准备 搭建Haoop分布式集群所需环境&#x…

数据结构和算法——线性结构

文章目录 前言线性表顺序表链表合并有序链表反转链表 队列循环队列双端队列资源分配问题 栈共享栈表达式求值递归处理迷宫问题 串串的模式匹配BF算法KMP算法next数组的求解next数组的优化 前言 本文所有代码均在仓库中&#xff0c;这是一个完整的由纯C语言实现的可以存储任意类…

Mybatis--动态sql

XML映射文件&#xff08;简单的SQL用注解&#xff0c;复杂的用xml&#xff09; 规范&#xff1a; XML映射文件的名称和Mapper接口名称一样&#xff08;同包同名&#xff09;注意&#xff1a;不能直接用.创建文件夹,用/分层 xml映射文件的namespace属性为mapper接口全限定名一致…

Python数据分析实战-实现T检验(附源码和实现效果)

实现功能 T 检验&#xff08;Students t-test&#xff09;是一种常用的统计方法&#xff0c;用于比较两个样本之间的均值是否存在显著差异。它可以应用于许多场景&#xff0c;其中一些常见的应用场景包括&#xff1a; A/B 测试&#xff1a;在市场营销和用户体验研究中&#xf…

迁移Linux服务器用户数据(将一个服务器的Linux用户数据迁移到另一个Linux服务器用户的流程)

文章目录 1、打包源Linux服务器用户的数据2、发送源Linux服务器用户的数据3、查看目的服务器用户接受到的数据 1、打包源Linux服务器用户的数据 先来到根目录&#xff0c;再使用tar命令打包数据&#xff1a;tar czvf root.zip.gz ./* 2、发送源Linux服务器用户的数据 在根目…

探秘PMP和六西格玛的不同:哪一个能为你的职业生涯加分?

今天&#xff0c;我们将带你深入了解一项相对冷门但价值不菲的证书——六西格玛黑带。 可能你曾听说过PMP&#xff0c;但相比之下&#xff0c;六西格玛黑带的资源分享似乎较少&#xff0c;考试内容却更为广泛深入。这里&#xff0c;让我为你详细解析这一考试&#xff0c;带你进…

Python操作Hive数据仓库

Python连接Hive 1、Python如何连接Hive&#xff1f;2、Python连接Hive数据仓库 1、Python如何连接Hive&#xff1f; Python连接Hive需要使用Impala查询引擎 由于Hadoop集群节点间使用RPC通信&#xff0c;所以需要配置Thrift依赖环境 Thrift是一个轻量级、跨语言的RPC框架&…

latex如何对.pdf格式的图片实现裁剪

目录 问题描述&#xff1a; 问题解决&#xff1a; 问题描述&#xff1a; 在使用draw.io进行绘图&#xff0c;导出的时候不知道为什么周围会有留白&#xff0c;比如下图&#xff1a; 在导入latex的时候&#xff0c;会因为两侧的留白导致整张图片缩小。 如果直接进行裁剪.pdf&a…

简要归纳UE5 Lumen全局光照原理

文章目录 一、Jim kajiya老爷子的渲染方程&#xff1a;二、工程上的实时全局光照技术&#xff1a;三、Lumen的解决办法&#xff1a;1、用距离场 Distance Field&#xff08;SDF&#xff09;判断光线和三角面相交&#xff1a;2.表面缓存&#xff08;Surface Cache&#xff09; 四…

《论文阅读:Dataset Condensation with Distribution Matching》

点进去这篇文章的开源地址&#xff0c;才发现这篇文章和DC DSA居然是一个作者&#xff0c;数据浓缩写了三篇论文&#xff0c;第一篇梯度匹配&#xff0c;第二篇数据增强后梯度匹配&#xff0c;第三篇匹配数据分布。DC是匹配浓缩数据和原始数据训练一次后的梯度差&#xff0c;DS…

Apache Shiro 漏洞复现

文章目录 Apache Shiro 漏洞复现1. Apache Shiro 1.2.4 反序列化漏洞1.1 漏洞描述1.2 漏洞原理1.3 漏洞复现1.3.1 环境启动 1.4 漏洞利用1.5 修复方案 Apache Shiro 漏洞复现 链接地址&#xff1a;Vulhub - Docker-Compose file for vulnerability environment 1. Apache Shi…

MySQL中使用函数会使索引失效?

文章目录 1、前置准备2、ChatGPT的答案3、实践证明SQL1SQL2SQL3SQL4SQL5 4、总结 1、前置准备 首先创建我们要测试的库表 CREATE TABLE lianhe_index (id int(11) NOT NULL AUTO_INCREMENT COMMENT id,name varchar(255) DEFAULT NULL,age int(11) DEFAULT NULL,number int(1…

相似与不同:数字孪生和元宇宙的对比

数字孪生和元宇宙是两个备受瞩目的概念&#xff0c;都在数字领域产生了巨大的影响。它们有一些相似之处&#xff0c;但也存在显著的不同。本文将介绍它们的相同点和不同点&#xff0c;以及它们在不同应用领域的前景。 1. 相同点 虚拟性质&#xff1a; 数字孪生和元宇宙都是虚…

AlphaPose Pytorch 代码详解(一):predict

前言 代码地址&#xff1a;AlphaPose-Pytorch版 本文以图像 1.jpg&#xff08;854x480&#xff09;为例对整个预测过程的各个细节进行解读并记录 python demo.py --indir examples/demo --outdir examples/res --save_img1. YOLO 1.1 图像预处理 cv2读取BGR图像 img [480,…

LATR:3D Lane Detection from Monocular Images with Transformer

参考代码&#xff1a;LATR 动机与主要工作&#xff1a; 之前的3D车道线检测算法使用诸如IPM投影、3D anchor加NMS后处理等操作处理车道线检测&#xff0c;但这些操作或多或少会存在一些负面效应。IPM投影对深度估计和相机内外参数精度有要求&#xff0c;anchor的方式需要一些如…

【图像融合】差异的高斯:一种简单有效的通用图像融合方法[用于融合红外和可见光图像、多焦点图像、多模态医学图像和多曝光图像](Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

淘宝价格,淘宝商品优惠券数据接口,淘宝商品销量接口,淘宝商品详情数据接口,淘宝API接口

淘宝价格和商品优惠券数据接口是淘宝平台提供的官方数据接口&#xff0c;通过调用接口&#xff0c;可以获取到淘宝商品的价格信息和优惠券数据。 获取淘宝价格和商品优惠券数据接口的步骤如下&#xff1a; 输入淘宝网址登陆淘宝账号密码。点击获取key和secret。调用获取buyer…

android 与 flutter 之间的通信

文章目录 前言集成 flutter 混合开发android 与 flutter 之间的通信总结 一、前言 因为flutter 具有跨平台的属性&#xff0c;既可以在android上跑&#xff0c;也能在ios 上跑&#xff0c;所以为了节约开发的成本&#xff0c;减少人力&#xff0c;势必就会用到它。然而已有的…