当前位置: 首页 > news >正文

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是一种预处理指令,他有两种用法:

  1. #define 定义常量(标识符)
  2. #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定义符号和宏时,需要涉及几个步骤

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

注意:

  1. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
  2. 当预处理器搜索#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))

那为什么不用函数来完成这个任务?

原因有二:

  1. 用于调用函数和从函数返回的代码可能实际执行这个小型计算工作所需要的时间更多
    所以宏比函数在程序的规模和速度方面更胜一筹
  2. 更为重要的是函数的参数必须声明为特定的类型
    所以函数只能在类型合适的表达式上使用
    宏是类型无关的

 宏的缺点:当然和函数相比,宏也有劣势的地方:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中,除非宏比较短,否则可能大幅度增加程序的长度
  2. 宏是没法调试的
  3. 宏由于类型无关,也就不够严谨
  4. 宏可能会带来运算符优先级的问题,导致过程容易出现错误

 宏有时候可以做函数做不到的事情,比如:宏的参数可以出现类型,但是函数做不到

​​​​​​​

对比

建议:

如果逻辑比较简单,可以使用宏来实现

如果计算逻辑比较负责,那么就使用函数实现 

**2.7内联函数 

C99之后,C++引入了内联函数的概念  inline关键字

内联函数具有函数和宏的双重优点:

  1. 内联函数是函数
  2. 内联函数又像宏一样,在调用的地方展开

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>。 函数原型&#xff1a; template<class Function, class... Args> std::future<std::invoke_result_t<std::decay_t<Function>,std::decay_t<Args>...>>async(std::launch polic…...

万物皆数——用matlab求解二阶微分方程

一、背景 毕达哥拉斯的“万物皆数”哲学观点表达了一个理念&#xff0c;即宇宙万物都可以通过数学语言来描述&#xff0c;数是万物的本原。 勾股定理就是毕达哥拉斯提出&#xff0c;因此在西方勾股定理也被叫做毕达哥拉斯定理。 工科类的专业&#xff0c;越到后面越感觉到数学…...

jmeter接口自动化部署jenkins教程

首先&#xff0c;保证本地安装并部署了jenkins&#xff0c;jmeter&#xff0c;xslproc 我搭建的自动化测试框架是jmeterjenkinsxslproc ---注意&#xff1a;原理是&#xff0c;jmeter自生成的报告jtl文件&#xff0c;通过xslproc工具&#xff0c;再结合jmeter自带的模板修改&…...

前端js实现将数组对象组装成自己需要的属性,或者去掉对象中不必要的属性

前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 需求&#xff1a;前端js实现将数组对象组装成自己需要的属性&#xff0c;或者前端js实现去掉对象中不必要的属性 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、示例数组对象…...

MeterSphere 任意文件读取漏洞(CVE-2023-25814)

MeterSphere 任意文件读取漏洞&#xff08;CVE-2023-25814&#xff09; 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: title"MeterSphere" 漏洞复现1. 构造poc2. 发送数据包3. 查看文件 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地…...

设计模式-01-单例设计模式

经典的设计模式有23种&#xff0c;但是常用的设计模式一般情况下不会到一半&#xff0c;我们就针对一些常用的设计模式进行一些详细的讲解和分析&#xff0c;方便大家更加容易理解和使用设计模式。 1-为什么要使用单例 单例设计模式&#xff08;Singleton Design Pattern&…...

霍尔电流传感器如何进行可靠性测试?主要应用在哪些领域?

霍尔电流传感器广泛应用于航空航天、电源监测、飞行器状态监测、变速驱动设备、焊接设备供电电源、新能源汽车蓄电池管理系统等领域&#xff0c;在电流检测领域中有着重要地位和实用价值&#xff0c;在电驱系统中被称为新能源汽车的动力“心脏”。因此&#xff0c;霍尔电流传感…...

pandas按行按列遍历Dataframe的三种方式

遍历数据有以下三种方法&#xff1a; 简单对上面三种方法进行说明&#xff1a; iterrows(): 按行遍历&#xff0c;将DataFrame的每一行迭代为(index, Series)对&#xff0c;可以通过row[name]对元素进行访问。 itertuples(): 按行遍历&#xff0c;将DataFrame的每一行迭代为元…...

Api接口如何防止被刷?

现在越来越多的应用程序和服务都提供了API接口&#xff0c;使得开发人员可以方便地与这些应用程序和服务进行交互。但是&#xff0c;由于API接口是公开的&#xff0c;因此很容易被黑客利用&#xff0c;对系统造成损害。为了确保API接口的安全性&#xff0c;我们需要采取一些措施…...

Django——orm模块创建表关系

django orm中如何创建表关系 1. 表关系分析 表与表之间的关系: 一对多 多对多 一对一 没有关系 判断表关系的方法: 换位思考用4张表举例: 图书表 出版社表 作者表 作者详情表图书和出版社是一对多的关系 外键字段建在多的那一方图书和作者是多对多的关系 需要创建第三张表来…...

Django知识点

目录 一、三板斧的使用 二、全局配置文件 三、静态文件的设置 四、request对象的方法 五、pycharm链接数据库 六、Django链接数据库 七、orm介绍 一、三板斧的使用 三个方法&#xff1a; HttpResponse renderredirect def index(request):print(request)return HttpR…...

基于单片机设计的智能风扇(红外线无线控制开关调速定时)

一、项目介绍 在炎热的夏季&#xff0c;风扇成为人们室内生活中必不可少的电器产品。然而&#xff0c;传统的风扇控制方式存在一些不便之处&#xff0c;比如需要手动操作开关、无法远程控制和调速&#xff0c;以及缺乏定时功能等。为了解决这些问题&#xff0c;设计了一款基于…...

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加密分析

前言 主要是报了自考在这个小程序上面做题&#xff0c;就研究了一下这个接口本文仅供学习交流使用&#xff0c;请勿随意传播。如有侵犯你的权益及时联系我删除。 一、抓包分析打开小程序&#xff0c;打开devtools 工具&#xff0c;这里就不啰嗦&#xff0c;直接上过程。 点击…...

加密算法笔记

MD5: 目前相对容易破解&#xff0c;且容易发生碰撞; 不可逆(但是网络上有很多破解方法) SHA256: 不会碰撞; 可以加盐&#xff0c;也可以不加盐; 不可逆; HmacSHA256在线加密-HmacMD5加密-散列哈希HASH加密工具 相同点 1、都是密码散列函数&#xff0c;加密不可逆。 2、都可…...

Sa-Token拦截全部接口必须登录-然后自定义注解来匿名登录-作为权限框架支持,并且同时使用了注解和路由的拦截器模式,此部分的配置如下:

场景 在博主目前正在开发的项目中&#xff0c;就采用了Sa-Token作为权限框架支持&#xff0c;并且同时使用了注解和路由的拦截器模式&#xff0c;此部分的配置如下&#xff1a; Override public void addInterceptors(InterceptorRegistry registry) { List<String> n…...

公司企业端口映射

文章目录 前言如何进行内网映射打开路由器管理网址设置端口映射 前言 公司为了提供稳定的网络服务&#xff0c;需要拥有一个稳定的IPV4的网络地址&#xff0c;一般公司有点规模的&#xff0c;会去电信局里面拉一根专门的网线&#xff0c;获取稳定的全球IPV4地址。 一个IPV4地…...

gitlab安装和使用

gitlab安装和使用 1.下载必要的依赖 sudo yum install curl openssh-server openssh-clients postfix cronie #需要执行sudo service postfix start #配置邮箱的sudo chkconfig postfix on #配置邮箱的#这句是用来做防火墙的&#xff0c;避免用户通过ssh方式和http来访问。su…...

DL/T 645 协议实战解析:从帧结构到数据采集

1. DL/T 645协议基础入门 第一次接触DL/T 645协议时&#xff0c;我也被那些专业术语搞得一头雾水。但实际用起来才发现&#xff0c;这个协议就像电力行业的"普通话"&#xff0c;专门用来让智能电表和采集设备顺畅交流。简单来说&#xff0c;它就是一套规定了电表怎么…...

AI专著撰写高效法:AI工具加持,20万字专著迅速成型!

学术专著创作困境与AI工具助力 学术专著的创作并非易事&#xff0c;它不仅仅关乎能否完成写作&#xff0c;更重要的是能否实现出版和获得认可。在当前的出版市场上&#xff0c;学术专著吸引的读者群体相对较小&#xff0c;这使得出版社在评估选题的学术价值时变得格外严谨&…...

2026年厦门寻味指南:这6家地道特产店,本地人私藏

在厦门&#xff0c;买特产是一门学问。游客扎堆的景区商业街&#xff0c;价格虚高、品质参差是常态。真正的老厦门人&#xff0c;自有他们信赖的“秘密基地”。这些店铺往往藏身于老城区、市场周边&#xff0c;靠的是口口相传的口碑和几十年如一日的诚信经营。今天&#xff0c;…...

英维思/康吉森TRICONEX 3721 (AI32TMR)模块

在工业自动化的精密世界里&#xff0c;每一个数据的精准传递都关乎生产的命脉。英维思TRICONEX 3721 AI32TMR模块&#xff0c;就像一位沉默的守护者&#xff0c;以三重冗余的硬核架构&#xff0c;为石油化工、电力能源等高危行业筑牢安全防线。李工180**6050**3853它诞生于对工…...

AI搜索优化不是SEO!一文看懂GEO服务商怎么挑

AI搜索优化不是SEO&#xff01;一文看懂GEO服务商怎么挑很多企业踩坑&#xff0c;就是把GEO当成SEO来选&#xff0c;用关键词排名、收录量、外链数判断效果&#xff0c;完全方向错误。核心区别一句话&#xff1a;SEO优化网页位置&#xff0c;GEO优化AI认知 SEO&#xff1a; 关键…...

Qt中调用相机进行拍照并实现图像处理

在Qt中调用相机进行拍照并实现图像处理&#xff0c;可以通过结合Qt Multimedia模块和图像处理库&#xff08;如OpenCV&#xff09;实现。一、相机调用与拍照&#xff08;Qt Multimedia模块&#xff09; 1. 环境配置 在Qt项目文件&#xff08;.pro&#xff09;中添加多媒体模块依…...

终极Windows安装媒体创建工具:一键支持1507到23H2全版本

终极Windows安装媒体创建工具&#xff1a;一键支持1507到23H2全版本 【免费下载链接】MediaCreationTool.bat Universal MCT wrapper script for all Windows 10/11 versions from 1507 to 21H2! 项目地址: https://gitcode.com/gh_mirrors/me/MediaCreationTool.bat 你…...

告别手速焦虑:大麦网Python自动化抢票脚本5分钟上手指南

告别手速焦虑&#xff1a;大麦网Python自动化抢票脚本5分钟上手指南 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 还在为心仪的演唱会门票秒光而烦恼吗&#xff1f;DamaiHelper大麦网抢票脚本为…...

Phi-3.5-mini-instruct惊艳效果:对gRPC Protobuf IDL生成Go/Python双端代码

Phi-3.5-mini-instruct惊艳效果&#xff1a;对gRPC Protobuf IDL生成Go/Python双端代码 1. 模型概述与核心能力 Phi-3.5-mini-instruct是微软推出的轻量级开源指令微调大模型&#xff0c;在长上下文代码理解&#xff08;RepoQA&#xff09;、多语言MMLU等基准测试中表现优异。…...

OpenCV实战:5行Python代码搞定图像二值化,大津法(OTSU)原来这么简单

OpenCV实战&#xff1a;5行Python代码玩转图像二值化&#xff0c;大津法(OTSU)的工程化实践 在文档扫描、工业质检这些需要高精度图像处理的场景里&#xff0c;工程师们常常遇到这样的困扰&#xff1a;拍摄环境的光照总是不均匀&#xff0c;传统固定阈值方法要么把阴影部分误判…...