C语言-程序环境和预处理(14.2)
目录
预处理详解
1.预定义符号
2. #define
2.1 #define定义标识符
2.2 #define 定义宏
2.3 #define 替换规则
注意事项:
2.4 #和##
2.5 带副作用的宏参数
2.6 宏和函数对比
3. #undef
4. 条件编译
4.1 单分支条件编译
4.2 多分支条件编译
4.3 判断是否被定义
5. 文件包含
5.1 本地文件包含
5.2 库函数包含
5.3 嵌套文件包含
写在最后:
预处理详解
思维导图:
1.预定义符号
例:
#include <stdio.h>int main()
{printf("%s\n", __FILE__);//进行编译的文件位置printf("%d\n", __LINE__);//文件当前的行号printf("%s\n", __DATE__);//文件被编译的日期printf("%s\n", __TIME__);//文件被编译的时间return 0;
}
输出:
输出:
F:\my code\c_plus_plus-code-exercise-warehouse\2023_2_8\2023_2_8\test.c
24
Feb 8 2023
19:49:32
注:输出的第一行是我这个文件的路径。
2. #define
2.1 #define定义标识符
#include <stdio.h>#define print printf
#define size sizeof
#define MAX 1000int main()
{print("hello world\n");print("%d\n", size(int));print("%d\n", MAX);return 0;
}
这个其实就是:
1. 用print 代替了 printf,
2. 用size 代替了 sizeof,
3. 用MAX 代替了 1000。
输出:
输出:
hello world
4
1000
另外,建议不要在#define 后面加分号,容易出事。
2.2 #define 定义宏
例:
#include <stdio.h>#define mul(x) ((x)*(x))int main()
{int x = 3;int ret = mul(x);printf("%d\n", ret);return 0;
}
输出:
输出:9
这个的本质其实就是将mul(x) 替换成 ((x)*(x))
当然,x可以是你指定的值。
例:
#include <stdio.h>#define mul(x) ((x)*(x))int main()
{int x = 3;int ret = mul(x);printf("%d\n", ret);ret = mul(3);printf("%d\n", ret);return 0;
}
输出:
输出:
9
9
所以这个也是一样的。
注:
1. 参数列表的左括号必须与mul(你定义的名称)紧邻。
2. 用于对数值表达式进行求值的宏定义建议加上()确保优先级。
2.3 #define 替换规则
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,
如果是,它们首先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。
对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号,
如果是,就重复上述处理过程。
注意事项:
1. 宏参数和#define 定义中可以出现其他#define 定义的符号,但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
2.4 #和##
例:
#define PRINT(format, x) printf("the value of "#x" is "format"\n", x)int main()
{int a = 10;//printf("the value of a is %d\n", a);PRINT("%d", a);//printf("the value of ""a"" is ""%d""\n", x)int b = 20;//printf("the value of b is %d\n", b);PRINT("%d", b);float f = 3.14f;PRINT("%f", f);//printf("the value of ""f"" is ""%f""\n", x)return 0;
}
输出:
输出:
the value of a is 10
the value of b is 20
the value of f is 3.140000
总结:这里就是使用 # ,把一个宏参数变成对应的字符串。
例2:
#include <stdio.h>#define CAT(x,y) x##yint main()
{int helloworld = 2023;printf("%d\n", CAT(hello, world));return 0;
}
输出:
输出:2023
总结:##可以把位于它两边的符号合成一个符号。
2.5 带副作用的宏参数
例:
#include <stdio.h>#define MAX(x, y) ((x)>(y)?(x):(y))int main()
{int a = 3;int b = 5;int m = MAX(a++, b++);//宏替换之后://((a++)>(b++)?(a++):(b++))printf("%d\n", m);printf("%d %d\n", a, b);return 0;
}
输出:
输出:
6
4 7
因为宏是直接将代码替换下来,所以在使用有副作用的参数时一定要小心。
使用函数的话,就不会出现这样的问题:
例:
#include <stdio.h>int max(int x, int y)
{return x > y ? x : y;
}int main()
{int a = 3;int b = 5;int m = max(a++, b++);printf("%d\n", m);printf("%d %d\n", a, b);return 0;
}
输出:
输出:
5
4 6
因为函数传参就是传的a和b过去计算。
2.6 宏和函数对比
属 性 | #define定义宏 | 函数 |
代 码 长 度 | 每次使用时,宏代码都会被插入到程序中。 除了非常小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方; 每次使用这个函数时, 都调用那个地方的同一份代码 |
执 行 速 度 | 更快 | 函数调用和返回有额外开销, 所以相对慢一些 |
操 作 符 优 先 级 | 宏参数的求值是: 在所有周围表达式的上下文环境里, 否则邻近操作符的优先级可能会产生 所以建议宏在书写的时候多些括号。 | 函数参数只在函数调用的时候 求值一次, 它的结果值传递给函数。 表达式求值结果更容易预测。 |
带 有 副 作 用 的 参 数 | 参数可能被替换到宏体中的多个位置, 所以带有副作用的参数求值, 可能会产生不可预料的结果。 | 函数参数只在传参的时候 求值一次,结果更容易控制。 |
参 数 类 型 | 宏的参数与类型无关, 只要对参数的操作是合法的, | 函数的参数是与类型有关的, 如果参数的类型不同, 就需要不同的函数, 即使他们执行的任务是 |
调 试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递 归 | 宏是不能递归的 | 函数是可以递归的 |
总结:
宏和函数各有优劣,根据实际场景权衡使用。
2.7 命名约定
把宏名全部大写
函数名不要全部大写
3. #undef
这条指令用于移除一个宏定义。
例:
我们发现,移除宏定义MAX之后,再次使用就报错了。
4. 条件编译
我们可以通过使用条件编译根据设定条件屏蔽掉我们不想要的代码。
4.1 单分支条件编译
例:
#include <stdio.h>#define PRINT int main()
{
#ifdef PRINT //还有一个#ifndef是表示PRINT未定义就执行printf("hehe\n");
#endifreturn 0;
}
输出 :
输出:hehe
4.2多分支条件编译:
例:
#include <stdio.h>#define PRINT 1int main()
{#if PRINT == 1printf("1");
#elif PRINT == 10printf("10");
#else printf("???");
#endif return 0;
}
输出:
输出:1
#include <stdio.h>#define PRINT 10int main()
{#if PRINT == 1printf("1");
#elif PRINT == 10printf("10");
#else printf("???");
#endif return 0;
}
输出:
输出:10
#include <stdio.h>#define PRINT 100int main()
{#if PRINT == 1printf("1");
#elif PRINT == 10printf("10");
#else printf("???");
#endif return 0;
}
输出:
输出:???
4.3 判断是否被定义
例:
#include <stdio.h>#define PRINT int main()
{
#if !defined(PRINT)printf("hehe\n");
#endif#if defined(PRINT)printf("haha\n");
#endifreturn 0;
}
输出:
输出:haha
当然,条件编译也支持嵌套。
在实际中,条件编译也有广泛的应用:
例:
我们可以看一个头文件的源码感受一下:
5. 文件包含
5.1 本地文件包含
先在源文件所在目录下查找,如果该头文件未找到,
编译器就像查找库函数头文件一样在标准位置查找头文件。
5.2 库函数包含
就直接去标准路径下去查找,如果找不到就提示编译错误。
5.3 嵌套文件包含
如果在包含头文件的时候出现这样的情况:
因为头文件展开后会将所以代码放开,
这样就会造成文件内容的重复。
我们可以用条件编译解决这样的问题:
例:
#ifndef __TEST_H__
#define __TEST_H__
//头文件具体内容:
//...
//
#endif
我们用这个条件编译将头文件的内容包起来,
当再次调用这个头文件的时候,
就会因为 __TEST_H__已经定义过了,而不再编译头文件内容。
当然,如果你嫌麻烦的话,
#pragma once
在头文件中写下这段代码也是同样的效果。
写在最后:
以上就是本篇文章的内容了,感谢你的阅读。
如果喜欢本文的话,欢迎点赞和评论,写下你的见解。
如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。
之后我还会输出更多高质量内容,欢迎收看。
相关文章:

C语言-程序环境和预处理(14.2)
目录 预处理详解 1.预定义符号 2. #define 2.1 #define定义标识符 2.2 #define 定义宏 2.3 #define 替换规则 注意事项: 2.4 #和## 2.5 带副作用的宏参数 2.6 宏和函数对比 3. #undef 4. 条件编译 4.1 单分支条件编译 4.2 多分支条件编译 4.3 判断是…...

VHDL语言基础-时序逻辑电路-计数器
目录 计数器的设计: 计数器的作用: 计数器的实现: 1、用“”函数描述: 用T触发器级联构成的串行进位的二进制加法计数器的仿真波形: 计数器的仿真: 计数器的设计: 计数是一种最简单基本的…...

MySQL数据库07——高级条件查询
前面一章介绍了基础的一个条件的查询,如果多条件,涉及到逻辑运算,and or 之类的。就是高级一点的条件查询。本章来介绍复杂的条件搜索表达式。 AND运算符 AND运算符只有当两边操作数均为True时,最后结果才为True。人们使用AND描述…...

《Terraform 101 从入门到实践》 第四章 States状态管理
《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看。 军书十二卷,卷卷有爷名。 为什么需要状态管理 Terraform的主要作用是管理云平台上的资源ÿ…...

数据结构之二叉树
🎈一.二叉树相关概念 1.树 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合,树结构通常用来存储逻辑关系为 "一对多" 的数据。例如: 关于树的几个重要概念&…...

上海亚商投顾:三大指数集体调整 消费板块逆市活跃
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。市场情绪三大指数今日集体调整,沪指全天弱势震荡,创业板指盘中跌超1%。旅游、食品、乳业等大消费板块…...

【2023unity游戏制作-mango的冒险】-开始画面API制作
👨💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:游戏制作 ⭐mango的冒险-开始画面制作⭐ 文章目录⭐mango的冒险-开始画面制作⭐👨&…...

【微服务】Nacos配置管理
🚩本文已收录至专栏:微服务探索之旅 👍希望您能有所收获 Nacos除了可以做配置管理,同样可以当作注册中心来使用。 了解注册中心用法点击跳转👉【微服务】Nacos注册中心 一.引入 当微服务部署的实例越来越多࿰…...

【C++】类与对象理解和学习(上)
专栏放在【C知识总结】,会持续更新,期待支持🌹类是什么?类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它(实例化后才…...

Pyqt5小案例,界面与逻辑分离的小计算器程序
直接看下最终效果: 使用技术总结 使用Designer设计界面 使用pyuic5命令导出到python文件 新建逻辑处理文件,继承pyuic5导出的文件的类,在里面编写信号与槽的处理逻辑 使用Designer设计界面 要使用Designer,安装一个Python库即…...

leaflet加载KML文件,显示图形(方法2)
第049个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中加载KML文件,将图形显示在地图上。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,请加载其他来练习 文章目录 示例效果配置方式示例源代码(共66…...

Mysql 部署 MGR 集群
0. 参考文章 官方文档: MySQL :: MySQL 8.0 Reference Manual :: 18.2 Getting Started 博客: MGR 单主模式部署教程(基于 MySQL 8.0.28) - 墨天轮 (modb.pro) mysql MGR单主模式的搭建 - 墨天轮 (modb.pro) MySQL 5.7 基于…...

迁移至其他美国主机商时需要考虑的因素
网站的可访问性是关系业务的关键因素之一。一个稳定、快速且优化良好的主机上的网站更有可能享受不间断的流量,并在谷歌的SERP中获得更好的排名。因此,在构建企业网站时,选择合适的主机商相当重要。不过就以美国主机为例,由于每个…...

【数据结构】第二章 线性表
文章目录第二章 知识体系2.1 线性表的定义和基本操作2.1.1 线性表的定义2.1.2 线性表的基本操作2.2 线性表的顺序表示2.2.1 顺序表的定义2.2.2 顺序表的基本操作的实现2.3 线性表的链式表示2.3.1 单链表的定义2.3.2 单链表的基本操作实现2.3.3 双链表2.3.4 循环链表2.3.5 静态链…...

RESTful API 为何成为顶流 API 架构风格?
作者孙毅,API7.ai 技术工程师,Apache APISIX Committer 万物互联的世界充满着各式各样的 API ,如何统筹规范 API 至关重要。RESTful API 是目前世界上最流行的 API 架构风格之一,它可以帮助你实现客户端与服务端关注点分离&#x…...
Python基础知识点汇总(列表)
列表的含义 列表由一系列按特定顺序排列的元素组成,是Python中内置的可变序列。 **注:**列表的所有元素放在中括号[]中,相邻的两个元素用逗号分隔; 可将整数、实数、字符串、列表、元组等任何类型的内容放到列表中,且同一列表的元素类型可以不同。 列表的创建和删除 1.…...

新的一年软件测试行业的趋势能够更好?
如果说,2022年对于全世界来说,都是一场极大的挑战的话;那么,2023年绝对是机遇多多的一年。众所周知,随着疫情在全球范围内逐步得到控制,无论是国际还是国内的环境,都会呈现逐步回升的趋势&#…...

Threejs中的Shadow Mapping(阴影贴图)
简而言之,步骤如下: 1.从灯光位置视点(阴影相机)创建深度图。 2.从相机的位置角度进行屏幕渲染,在每个像素点,比较由阴影相机的MVP矩阵计算的深度值和深度图的值的大小,如果深度图值小的话&…...

本质安全设备标准(IEC60079-11)的理解(四)
本质安全设备标准(IEC60079-11)的理解(四) 对于标准中“Separation”的理解 IEC60079-11使用了较长的篇幅来说明设计中需要考虑到的各种间距, 这也从一定程度上说明了间距比较重要,在设计中是需要认真考虑…...

(record)QEMU安装最小linux系统——TinyCore(命令行版)
文章目录QEMU安装最小linux系统——TinyCore参考QEMU使用qemu创建tinycore虚拟机再次启动文件保存QEMU安装最小linux系统——TinyCore 简单记录安装过程和记录点 参考 [原创] qemu 与 Tiny Core tinycore的探索 QEMU qemu不多介绍,这里是在WSL2上安装的linux版…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...