C语言 预处理详解
目录
1.预定义符号
2.#define
2.1#define 定义标识符
2.2#define 定义宏
2.3#define 替换规则
2.4#和##
2.4.1# 的作用
2.4.2## 的作用
2.5 带有副作用的宏参数
2.6宏和函数的对比
对比
**2.7内联函数
2.8命名约定
3.#undef
**4.命令行定义
5.条件编译
常见的条件编译指令
6.头文件包含
6.1头文件被包含的方式
6.2嵌套文件的包含
1.预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义符号都是语言内置的
举个例子:

2.#define
#define是一种预处理指令,他有两种用法:
- #define 定义常量(标识符)
- #define 定义宏
2.1#define 定义标识符
语法:
#define name stuff
举个例子:

#define 是完全替换,比如

所以在定义的时候,为了强调他是一个整体,需要自己带上括号:

注意:由于是完全替换,在define定义标识符的时候,不要在最后加 ; 否则替换的时候会将 ; 也替换过去,会导致语法错误
2.2#define 定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常会被解释为宏(macro)或定义宏(define macro)
下面是宏的声明方式:
#define name( parament-list ) stuff
其中的parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中
注意:
参数列表的左括号必须与name紧邻
如果两者之间有任何空白存在,参数列表就会被释解释为stuff的一部分
如:

#define定义宏也是完全替换,比如:

为了防止出现失误,我们在声明的时候需要加上括号:

我们在写宏的时候,如果逻辑需要,我们可以加上足够多的括号来使宏变得完整
2.3#define 替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,他们首先被替换
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程
注意:
- 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
2.4#和##
2.4.1# 的作用
如何把参数插入到字符串中?、

我们发现字符串是有自动连接的特点的
假设有这样的代码:

我们如何用宏来实现printf的功能呢,这里我们使用#

他的替换是周怎么完成的呢

这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中
使用#,把一个宏参数变成对应的字符串
比如:代码中的#N会被预处理器处理为:“N”
所以“#N”即被处理为““N””
2.4.2## 的作用
##可以把位于他两边的符号合成一个符号
他允许宏定义从分离的文本片段创建标识符

注意:这样的连接必须产生一个合法的标识符,否则其结果就是未定义的
2.5 带有副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的有永久性效果
x+1;//不带副作用
x++;//带副作用
MAX宏可以证明具有副作用的参数所引起的问题

这段代码输出的结果是什么?
这里我们得知道预处理之后的结果是什么:
这段代码是证明执行的呢?

2.6宏和函数的对比
宏通常被应用于执行简单的运算
比如在两个数中找出较大的一个
#define MAX(a, b) ((a)>(b)?(a):(b))
那为什么不用函数来完成这个任务?
原因有二:
- 用于调用函数和从函数返回的代码可能实际执行这个小型计算工作所需要的时间更多
所以宏比函数在程序的规模和速度方面更胜一筹 - 更为重要的是函数的参数必须声明为特定的类型
所以函数只能在类型合适的表达式上使用
宏是类型无关的
宏的缺点:当然和函数相比,宏也有劣势的地方:
- 每次使用宏的时候,一份宏定义的代码将插入到程序中,除非宏比较短,否则可能大幅度增加程序的长度
- 宏是没法调试的
- 宏由于类型无关,也就不够严谨
- 宏可能会带来运算符优先级的问题,导致过程容易出现错误
宏有时候可以做函数做不到的事情,比如:宏的参数可以出现类型,但是函数做不到

对比

建议:
如果逻辑比较简单,可以使用宏来实现
如果计算逻辑比较负责,那么就使用函数实现
**2.7内联函数
C99之后,C++引入了内联函数的概念 inline关键字
内联函数具有函数和宏的双重优点:
- 内联函数是函数
- 内联函数又像宏一样,在调用的地方展开
2.8命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者
那我们平时的一个习惯是:
- 把宏名全部大写 //MAX
- 函数名不要全部大写 //Max
3.#undef
这条指令用于移除一个宏定义

**4.命令行定义
许多C的编译器提供了一种能力,允许在命令行中定义符号,用于启动编译过程
例如:当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性有点用处
(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一 个机器内存大写,我们需要一个数组能够大写。)
5.条件编译
在编译一个程序序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的,因为我们有条件编译指令
条件编译就是:满足条件就编译,不满足条件就不编译
比如说:
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译

常见的条件编译指令
1.
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
表达式为真则编译,为假则不编译

2.多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
只会选择以一个#if或者#elif执行

3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
判断(symbol)是否被定义过,如果被定义过则执行代码


4.嵌套指令
#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
注意:#if 与 #endif 是配套使用的,同时出现,同时消失
6.头文件包含
我们已经知道,#include 指令可以使另外一个文件被编译,就像它实际出现于 #include 指令的地方一样
这种替换的方式很简单:
预处理器先删除这条指令,并用包含文件的内容替换
这样一个源文件被包含10次,那就实际被编译10次
6.1头文件被包含的方式
头文件的包含一般有两种方式:
1.包含本地文件(自己的.h文件)
#include "xxx.h"(用双引号)2.包含标准库中的文件
#include <xxx.h> (用尖括号)
查找策略:
先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件
如果找不到就提示编译错误
6.2嵌套文件的包含
如果出现这样的场景

comm.h和comm.c是公共模块
test1.h和test1.c使用了公共模块
test2.h和test2.c使用了公共模块
test.h和test.c使用了test1模块和test2模块。
这样最终程序中就会出现两份comm.h的内容
这样就造成了文件内容的重复
我们可以用条件编译解决这个问题
每个头文件的开头写:
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
或者
#pragma once

就可以避免头文件的重复引入
相关文章:
C语言 预处理详解
目录 1.预定义符号 2.#define 2.1#define 定义标识符 2.2#define 定义宏 2.3#define 替换规则 2.4#和## 2.4.1# 的作用 2.4.2## 的作用 2.5 带有副作用的宏参数 2.6宏和函数的对比 对比 **2.7内联函数 2.8命名约定 3.#undef **4.命令行定义 5.条件编译 常…...
c++ async 使用详解,创建异步任务的多种方法
c async 使用详解 std::async 头文件 #include <future>。 函数原型: template<class Function, class... Args> std::future<std::invoke_result_t<std::decay_t<Function>,std::decay_t<Args>...>>async(std::launch polic…...
万物皆数——用matlab求解二阶微分方程
一、背景 毕达哥拉斯的“万物皆数”哲学观点表达了一个理念,即宇宙万物都可以通过数学语言来描述,数是万物的本原。 勾股定理就是毕达哥拉斯提出,因此在西方勾股定理也被叫做毕达哥拉斯定理。 工科类的专业,越到后面越感觉到数学…...
jmeter接口自动化部署jenkins教程
首先,保证本地安装并部署了jenkins,jmeter,xslproc 我搭建的自动化测试框架是jmeterjenkinsxslproc ---注意:原理是,jmeter自生成的报告jtl文件,通过xslproc工具,再结合jmeter自带的模板修改&…...
前端js实现将数组对象组装成自己需要的属性,或者去掉对象中不必要的属性
前言 提示:这里可以添加本文要记录的大概内容: 需求:前端js实现将数组对象组装成自己需要的属性,或者前端js实现去掉对象中不必要的属性 提示:以下是本篇文章正文内容,下面案例可供参考 一、示例数组对象…...
MeterSphere 任意文件读取漏洞(CVE-2023-25814)
MeterSphere 任意文件读取漏洞(CVE-2023-25814) 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: title"MeterSphere" 漏洞复现1. 构造poc2. 发送数据包3. 查看文件 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地…...
设计模式-01-单例设计模式
经典的设计模式有23种,但是常用的设计模式一般情况下不会到一半,我们就针对一些常用的设计模式进行一些详细的讲解和分析,方便大家更加容易理解和使用设计模式。 1-为什么要使用单例 单例设计模式(Singleton Design Pattern&…...
霍尔电流传感器如何进行可靠性测试?主要应用在哪些领域?
霍尔电流传感器广泛应用于航空航天、电源监测、飞行器状态监测、变速驱动设备、焊接设备供电电源、新能源汽车蓄电池管理系统等领域,在电流检测领域中有着重要地位和实用价值,在电驱系统中被称为新能源汽车的动力“心脏”。因此,霍尔电流传感…...
pandas按行按列遍历Dataframe的三种方式
遍历数据有以下三种方法: 简单对上面三种方法进行说明: iterrows(): 按行遍历,将DataFrame的每一行迭代为(index, Series)对,可以通过row[name]对元素进行访问。 itertuples(): 按行遍历,将DataFrame的每一行迭代为元…...
Api接口如何防止被刷?
现在越来越多的应用程序和服务都提供了API接口,使得开发人员可以方便地与这些应用程序和服务进行交互。但是,由于API接口是公开的,因此很容易被黑客利用,对系统造成损害。为了确保API接口的安全性,我们需要采取一些措施…...
Django——orm模块创建表关系
django orm中如何创建表关系 1. 表关系分析 表与表之间的关系: 一对多 多对多 一对一 没有关系 判断表关系的方法: 换位思考用4张表举例: 图书表 出版社表 作者表 作者详情表图书和出版社是一对多的关系 外键字段建在多的那一方图书和作者是多对多的关系 需要创建第三张表来…...
Django知识点
目录 一、三板斧的使用 二、全局配置文件 三、静态文件的设置 四、request对象的方法 五、pycharm链接数据库 六、Django链接数据库 七、orm介绍 一、三板斧的使用 三个方法: HttpResponse renderredirect def index(request):print(request)return HttpR…...
基于单片机设计的智能风扇(红外线无线控制开关调速定时)
一、项目介绍 在炎热的夏季,风扇成为人们室内生活中必不可少的电器产品。然而,传统的风扇控制方式存在一些不便之处,比如需要手动操作开关、无法远程控制和调速,以及缺乏定时功能等。为了解决这些问题,设计了一款基于…...
k8s报错pause 3.2 解决方案
报错 Failed to create pod sandbox: rpc error: code Unknown desc failed to get sandbox image "k8s.gcr.io/pause:3.2": failed to pull image "k8s.gcr.io/pause:3.2": failed to pull and unpack image "k8s.gcr.io/pause:3.2": failed…...
基于遗传算法的电器分类,基于GA的电器分类
目录 背影 遗传算法的原理及步骤 基本定义 编码方式 适应度函数 运算过程 代码 结果分析 展望 完整代码下载链接:基于遗传算法的电器识别,基于遗传算法的电器分类(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88506649 背影 电器…...
某XX自考小程序的AES加密分析
前言 主要是报了自考在这个小程序上面做题,就研究了一下这个接口本文仅供学习交流使用,请勿随意传播。如有侵犯你的权益及时联系我删除。 一、抓包分析打开小程序,打开devtools 工具,这里就不啰嗦,直接上过程。 点击…...
加密算法笔记
MD5: 目前相对容易破解,且容易发生碰撞; 不可逆(但是网络上有很多破解方法) SHA256: 不会碰撞; 可以加盐,也可以不加盐; 不可逆; HmacSHA256在线加密-HmacMD5加密-散列哈希HASH加密工具 相同点 1、都是密码散列函数,加密不可逆。 2、都可…...
Sa-Token拦截全部接口必须登录-然后自定义注解来匿名登录-作为权限框架支持,并且同时使用了注解和路由的拦截器模式,此部分的配置如下:
场景 在博主目前正在开发的项目中,就采用了Sa-Token作为权限框架支持,并且同时使用了注解和路由的拦截器模式,此部分的配置如下: Override public void addInterceptors(InterceptorRegistry registry) { List<String> n…...
公司企业端口映射
文章目录 前言如何进行内网映射打开路由器管理网址设置端口映射 前言 公司为了提供稳定的网络服务,需要拥有一个稳定的IPV4的网络地址,一般公司有点规模的,会去电信局里面拉一根专门的网线,获取稳定的全球IPV4地址。 一个IPV4地…...
gitlab安装和使用
gitlab安装和使用 1.下载必要的依赖 sudo yum install curl openssh-server openssh-clients postfix cronie #需要执行sudo service postfix start #配置邮箱的sudo chkconfig postfix on #配置邮箱的#这句是用来做防火墙的,避免用户通过ssh方式和http来访问。su…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
