【C语言预处理详解(下)】--#和##运算符,命名约定,命令行定义 ,#undef,条件编译,头文件的包含,嵌套文件包含,其他预处理指令
目录
五.#和##运算符
5.1--#运算符
5.2--##运算符
六.命名约定,#undef,命令行定义
6.1--命名约定
6.2--#undef
6.3--命名行定义
七.条件编译
常见的条件编译指令:
1.普通的条件编译:
2.多个分支的条件编译(可以利用条件语句来辅助理解):
3.判断是否被定义:
4.嵌套指令:
八.头文件的包含
8.1--头文件被包含的方式:
8.1.1--本地文件包含
8.1.2--库文件包含
8.2--嵌套文件包含
九.其他预处理指令
🔥个人主页:@草莓熊Lotso的个人主页
🎬作者简介:C++研发方向学习者
📖个人专栏:《C语言》
⭐️人生格言:生活是默默的坚持,毅力是永久的享受。
五.#和##运算符
5.1--#运算符
#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。
#运算符所执⾏的操作可以理解为”字符串化“。
当我们有⼀个变量 int a = 10; 的时候,我们想打印出: the value of a is 10 .
# define PRINT(n) printf( "the value of " #n " is " format "\n" , n);
具体代码演示:
#include<stdio.h>
#define PRINT(n, format) printf("the value of "#n" is "format"\n", n)int main()
{int a = 10;//printf("the value of a is %d\n", a);PRINT(a, "%d");float f = 3.14f;//printf("the value of f is %f\n", f);PRINT(f, "%f");return 0;
}

我们在这里主要分析一下变量a,我们定义好宏后,用上面的方式去调用它,我们把a替换到了宏的体内,就出现了#a,而#a转换为"a"这一字符串,然后format也被"%d"替换。
我们还是看看PRINT(a, "%d");这一代码预处理之后的形式:
printf ( "the value of " "a" " is " "%d" "\n" , a);
运行代码就得出来我们想要的结果。
5.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); \}
我们来使用这个宏,定义不同函数试试:
GENERIC_MAX( int ) // 替换到宏体内后 int##_max ⽣成了新的符号 int_max 做函数名GENERIC_MAX( float ) // 替换到宏体内后 float##_max ⽣成了新的符号 float_max 做函数名int main (){// 调⽤函数int m = int_max( 8 , 9 );printf ( "%d\n" , m);float fm = float_max( 6 .5f , 8 .5f );printf ( "%f\n" , fm);return 0 ;}
最终代码实现:
#include<stdio.h>
#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(8, 9);printf("%d\n", m);float fm = float_max(6.5f, 8.5f);printf("%f\n", fm);return 0;
}
这里简单的举个例子帮助大家理解一下##运算符的使用,还想要更深入了解的可以去查阅一下相关资料来学习。
六.命名约定,#undef,命令行定义
6.1--命名约定
- 把宏名全部⼤写
- 函数名不要全部⼤写
6.2--#undef
--我们通常用这条指令去移除一个宏定义
# undef NAME// 如果现存的⼀个名字需要被重新定义,那么它的旧名字⾸先要被移除。
我们来看两个实际例子直观的了解一下它的使用:
例一:
#include <stdio.h>#define M 100int main()
{printf("%d\n", M);
#undef M#define M 1000printf("%d\n", M);return 0;
}
例二:
#include<stdio.h>
#define SQUARE(x) ((x) * (x))int main()
{int a = 10;int ret=SQUARE(a);printf("%d\n", ret);
#undef SQUARE(x)#define SQUARE(x) ( ( x ) + ( x ) )int ret2 = SQUARE(a);printf("%d\n", ret2);}
6.3--命名行定义
#include <stdio.h>
int main()
{int array[ARRAY_SIZE];int i = 0;for (i = 0; i < ARRAY_SIZE; i++){array[i] = i;}for (i = 0; i < ARRAY_SIZE; i++){printf("%d ", array[i]);}printf("\n");return 0;
}
编译指令(linux环境演示):
//linux 环境演⽰gcc -D ARRAY_SIZE=10 programe.c
七.条件编译
#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;
}
常见的条件编译指令:
1.普通的条件编译:
# if 常量表达式//...# endif// 常量表达式由预处理器求值。如:# define __DEBUG__ 1# if __DEBUG__//..# endif
2.多个分支的条件编译(可以利用条件语句来辅助理解):
#if 常量表达式
//...# elif 常量表达式//...# else//...# endif
3.判断是否被定义:
//如果被定义了就继续# if defined(symbol)# ifdef symbol//如果没被定义就继续# if !defined(symbol)# ifndef symbol
4.嵌套指令:
# if defined(OS_UNIX)# ifdef OPTION1unix_version_option1();# endif# ifdef OPTION2unix_version_option2();# endif# elif defined(OS_MSDOS)# ifdef OPTION2msdos_version_option2();# endif# endif
八.头文件的包含
8.1--头文件被包含的方式:
8.1.1--本地文件包含
1. #include "filename"
- 先在源⽂件所在⽬录下查找,如果该头⽂件未找到,编译器就像查找库函数头⽂件⼀样在标准位置查找头⽂件。
- 如果找不到就提示编译错误。
1. /usr/include
VS环境的标准头⽂件的路径:
1. C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
2. // 这是 VS2013 的默认路径
我们这里注意一下,要按照自己的安装路径去找。
8.1.2--库文件包含
1. #include <filename.h>
8.2--嵌套文件包含
我们已经知道, #include 指令可以使另外⼀个⽂件被编译。就像它实际出现于 #include 指令的地⽅⼀样。
这种替换的⽅式很简单:预处理器先删除这条指令,并⽤包含⽂件的内容替换。
我们来看看实际例子吧~
test.c:
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"int main()
{return 0;
}
test.h:
void test();struct Stu
{char name[20];int age;float score;};
这里给大家分享两种方法:
方法一:在每个文件的开头写如下内容
# ifndef __TEST_H__# define __TEST_H__// 头⽂件的内容# endif
这个方式首先判断test.h有没有被定义,如果没有就定义一次,这样之后下次再重复出现test.h也没有用了,不符合这里的条件判断,所有可以有效的避免头文件的重复引入。
方法二:新式的现代写法
# pragma once
利用这种方式同样也可以有效的避免头文件的重复引入,更简洁一点。
注意:这里给大家推荐一下《⾼质量C/C++编程指南》中附录的考试试卷的两个笔试题
- 头⽂件中的 ifndef/define/endif是⼲什么⽤的?
- #include <filename.h> 和 #include "filename.h" 有什么区别?
如果大家有兴趣的话可以在评论区留言后私信我,我把这本书分享给大家,这其中附录的考试试卷很有参考价值,比较重要。
九.其他预处理指令
#error
#pragma
# line...这里就不做介绍了,感兴趣的可以⾃⼰去了解一下,建议参考《C语⾔深度解剖》学习。# pragma pack() //在结构体部分介绍过,这个指令可以改变编译器的默认对齐数
往期回顾:
【C语言预处理详解(上)】--预定义符号,#define定义常量,#define定义宏,带有副作用的宏参数,宏替换的规则,宏和函数的对比
【C语言编译与链接】--翻译环境和运行环境,预处理,编译,汇编,链接
【通关文件操作(上)】--文件的意义和概念,二进制文件和文本文件,文件的打开和关闭,文件的顺序读写 【通关文件操作(下)】--文件的顺序读写(续),sprintf和sscanf函数,文件的随机读写,文件缓冲区,更新文件
结语:本篇文章就到此结束了,继前面一篇文章后,在此篇文章中给大家分享了预处理详解中的剩余知识点,如#和##运算符,命名约定,命令行定义 ,#undef,条件编译,头文件的包含,嵌套文件包含,其他预处理指令等,到此C语言专栏的知识点分享也差不多结束了,后续笔者还会继续分享其它的内容,如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家长久以来的支持。
相关文章:

【C语言预处理详解(下)】--#和##运算符,命名约定,命令行定义 ,#undef,条件编译,头文件的包含,嵌套文件包含,其他预处理指令
目录 五.#和##运算符 5.1--#运算符 5.2--##运算符 六.命名约定,#undef,命令行定义 6.1--命名约定 6.2--#undef 6.3--命名行定义 七.条件编译 常见的条件编译指令: 1.普通的条件编译: 2.多个分支的条件编译(可以利用条…...

03.搭建K8S集群
K8S集群搭建的方式 目前主流的搭建k8s集群的方式有kubeadm、minikube、二进制包三种方式: kubeadm(本案例搭建方式) 是一个工具,用于快速搭建kubernetes集群,目前应该是比较方便和推荐的,简单易用 kubea…...

RDMA简介3之四种子协议对比
RDMA协议共有四种子协议,分别为InfiniBand、iWARP、RoCE v1和RoCE v2协议。这四种协议使用统一的RDMA API,但在具体的网络层级实现上有所不同,如图1所示,接下来将分别介绍这四种子协议。 图1 RDMA四种子协议网络层级关系图 Infin…...

【最新版】西陆洗车系统源码全开源+uniapp前端+搭建教程
一.系统介绍 一款基于ThinkPHPUniapp开发的多门店洗车系统,包含用户端(小程序)、门店员工端(小程序)、门店端(PC)、平台管理端(PC)。 门店分连锁门店和独立门店…...
力扣LeetBook数组和字符串--二维数组
1.旋转矩阵 题目链接 想了那么久的各种旋转,对角线,其实把问题搞复杂了。 旋转90度的本质无非就是转置镜像对称 转置是什么?:将矩阵的行和列互换。 镜像对称:把矩阵从中间对折,互换位置 矩阵 A A [ 1 3 0…...

Linux开发工具(apt,vim,gcc)
目录 yum/apt包管理器 Linux编辑器 vim 1.见一见vim 2.vim的多模式 3.命令模式底行模式等 4.vim的配置 Linux编译器 gcc/g 1.预处理(宏替换) 2.编译(生成汇编) 3.汇编(生成机器可识别代码) 4.连…...
C# ExcelWorksheet 贴图
C# ExcelWorksheet 贴图 在C#中,如果你想在Excel工作表中插入图片(例如,在ExcelWorksheet中贴图),你可以使用ClosedXML或EPPlus这样的库来操作Excel文件。下面我将分别介绍如何使用这两个库来实现这一功能。 使用ClosedXML 首先,确保你已经安装了ClosedXML包。你可以通…...

鸿蒙Next开发真机调试签名申请流程
背景: 在学习鸿蒙next开发应用的初期总是会遇到一堆的问题,毕竟鸿蒙next开发不管是他的ArKTS语言还是他的开发工具DevEco Studio都还在起步阶段,就像当初的Android起步一样,总会有资料不足的一些问题。就比如我们学习下载完DevEco…...

[yolov11改进系列]基于yolov11引入上下文锚点注意力CAA的python源码+训练源码
【CAA介绍】 本文记录的是基于CAA注意力模块的RT-DETR目标检测改进方法研究。在远程遥感图像或其他大尺度变化的图像中目标检测任务中,为准确提取其长距离上下文信息,需要解决大目标尺度变化和多样上下文信息时的不足的问题。CAA能够有效捕捉长距离依赖…...
【Elasticsearch】 查询优化方式
在优化Elasticsearch的查询性能时,可以从多个维度着手,包括索引设计、查询优化、集群配置、数据管理以及监控分析等。常见的优化方式和策略有以下几种: 一、索引优化 合理设计字段类型: 字段类型选择: 对于不需要分词的…...
Xcode 16.4 + iOS 18 系统运行时崩溃:___cxa_current_primary_exception 符号丢失的原因与解决方案
在使用 Xcode 16.4 构建项目,运行到 iOS 18.3 或更早版本系统(包括模拟器)时,出现了如下的运行时崩溃: dyld[22183]: Symbol not found: ___cxa_current_primary_exceptionReferenced from: /.../WidgetOn.app/Widget…...

【linux】全志Tina预编译一个so库文件到根文件系统/usr/lib/下
一、sdk中新建文件夹 路径: V:\t113\work3\t113\openwrt\package\feeds\libs\md5util md5util为需要注入的库文件夹。 文件结构 libs md5util files libmd5util.so makefile etc.. 二、编写makefile include $(TOPDIR)/rules.mkPKG_NAME : md5util PKG_VERSIO…...

C# 类和继承(成员访回修饰符)
成员访回修饰符 本章之前的两节阐述了类的可访问性。对类的可访问性,只有两种修饰符:internal和public。 本节阐述成员的可访问性。类的可访问性描述了类的可见性;成员的可访问性描述了类成员的可 见性。 声明在类中的每个成员对系统的不同…...
c++ stl容器之map用法
目录 (1)map介绍 (2)map、multimap、unordered_map区别 (3)map用法 1.map接口表 2.使用举例 插入数据与遍历数据 查找关键字和值 删除元素 按照值排序 (4)multimap用法 &…...

Linux-文件管理及归档压缩
1.根下的目录作用说明: /:Linux系统中所有的文件都在根下/bin:(二进制命令目录)存放常用的用户命令/boot:系统启动时的引导文件(内核的引导配置文件,grub配置文件,内核配置文件) 例…...
结合Jenkins、Docker和Kubernetes等主流工具,部署Spring Boot自动化实战指南
基于最佳实践的Spring Boot自动化部署实战指南,结合Jenkins、Docker和Kubernetes等主流工具,提供从环境搭建到生产部署的完整流程: 一、环境准备与工具选型 1.基础设施 Jenkins服务器:安装Jenkins LTS版本,配置JDK(推荐JDK 11+)及Maven/Gradle插…...

微软认证考试科目众多?该如何选择?
在云计算、人工智能、数据分析等技术快速发展的今天,微软认证(Microsoft Certification)已成为IT从业者、开发者、数据分析师提升竞争力的重要凭证。但面对众多考试科目,很多人不知道如何选择。本文将详细介绍微软认证的考试方向、…...
MCP协议在LLM系统中的架构与实现原理研究
MCP协议的角色和功能定位 模型上下文协议(Model Context Protocol, MCP) 是由Anthropic公司(Claude模型的发布方)提出的一种开放协议,旨在标准化大型语言模型(LLM)与外部数据源、工具和服务之间的交互方式。可以将MCP类比为AI应用的“USB-C接口”:通过统一的接口协议,…...

Dify工作流实践—根据word需求文档编写测试用例到Excel中
前言 这篇文章依赖到的操作可查阅我之前的文章: dify里的大模型是怎么添加进来的:在Windows本地部署Dify详细操作 flask 框架app.route()函数的开发和调用:PythonWeb开发框架—Flask工程创建和app.route使用详解 结构化提示词的编写&…...

【LC实战派】小智固件编译
这篇写给立创吴总,是节前答应他配合git代码的说明;也给所有对小智感兴趣的小伙伴。 请多提意见,让这份文档更有价值 - 第一当然是拉取源码 - git clone https://github.com/78/xiaozhi-esp32.git 完成后,先查看固件中实际的…...
HTTP(超文本传输协议)详解
目录 一、基本概念 二、HTTP报文(结构) (一) 请求报文 (二) 响应报文 三、HTTP请求方法 1. GET方法 2. POST方法 3. PUT方法 4. HEAD方法 5. DELETE 6. OPTIONS 一、知识扩展 7. TRACE 8. CONNECT 四、HTTP持久通信 (一) HTTP keep-alive…...
Unity安卓平台开发,启动app并传参
using UnityEngine; using System;public class IntentReceiver : MonoBehaviour {public bool isVR1;void Start(){Debug.LogError("app1111111111111111111111111");if (isVR1){LaunchAnotherApp("com.HappyMaster.DaKongJianVR2");}else{// 检查是否有传…...

jdbcTemplate.query备忘录
jdbcTemplate.query中使用全部字符串和参数注入, 查询速度为什么差距这么大 如何正确使用JdbcTemplate参数化查询 1、使用?占位符 String sql "SELECT * FROM users WHERE name LIKE ?"; List<User> users jdbcTemplate.query(sql,new Object[…...

如何搭建Z-Blog PHP版本:详细指南
Z-Blog是一款功能强大且易于使用的博客平台,支持PHP和ASP两种环境。本文将重点介绍如何在PHP环境下搭建Z-Blog博客系统,帮助您快速上线自己的个人博客站点。 准备工作 1. 获取Z-Blog PHP版本 首先,访问Z-Blog官方网站下载最新版本的Z-Blog…...
Docker 常用命令详解
🐳 Linux 安装 Docker 及常用命令详解(适用于 Ubuntu) 📦 一、安装 Docker(Ubuntu 系统) 1. 更新系统 sudo apt update sudo apt upgrade -y2. 安装必要依赖 sudo apt install -y apt-transport-https …...
uniapp uni-id-co errCode“:“uni-id-captcha-required“,“errMsg“:“Captcha required
连续登录失败后就会出现图形验证码校验,如果前端不需要图形验证码校验,uni-id-co文件夹下找到module下的login文件夹下的login.js,注释掉Captcha相关校验,关掉即可 const {preLoginWithPassword,postLogin } require(../../lib/utils/login)…...

Github Copilot新特性:Copilot Spaces-成为某个主题的专家
概述 当今的工程团队都会面临知识碎片化的问题。关键的上下文分散在代码、文档和团队成员的头脑中,这使得他们很难在一个新的领域快速上手并完成工作。Copilot Spaces 通过集中您的项目上下文解决了这个问题,因此 Copilot 可以根据您的工作提供更智能、…...
React 第五十三节 Router中 useRouteError 的使用详解和案例分析
前言 useRouteError 是 React Router v6.4 引入的关键错误处理钩子,用于在 路由错误边界(Error Boundary) 中获取路由操作过程中发生的错误信息。 它提供了优雅的错误处理机制,让开发者能够创建用户友好的错误界面。 一、useRou…...
12分钟讲解主流React库
本内容是对 Every React Library Explained in 12 Minutes 内容的翻译与整理。 React Router React Router 是一个用于控制网站导航的库,同时也允许你自定义网站的 URL。它使用自定义组件,如 BrowserRouter、Routes 和 Route 组件,以创建 UR…...
《doubao-lite-32k 模型缓存机制使用指南》
doubao-lite-32k 模型缓存机制使用指南 一、缓存概述 1. 缓存作用 doubao-lite-32k 模型的缓存(Session 缓存)主要用于多轮对话场景,实现以下功能: 存储历史对话信息(Token),避免重复传输上下文,减少计算资源消耗。 优化长上下文(最长 32K Token)处理效率,提升多…...