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

预处理指令详解

预处理指令详解

    • **1.预定义符号**
    • **2.#define**
      • **2.1 #define 定义标识符**
      • **2.2 #define 定义宏**
      • **2.3 #define 替换规则**
      • **2.4 #和##**
        • **#的作用**
        • **##的作用**
      • **2.5 带副作用的宏参数**
    • **2.6 宏和函数的对比**
        • **宏和函数对比图**
      • **2.7 命名约定**
    • **3.#undef**
    • 4.条件编译
      • 4.1 常见的条件编译指令
    • 5.#error

1.预定义符号

__FILE__  //进行编译的源文件
__LINE__  //文件当前的行号
__DATE__  //文件被编译的日期
__TIME__  //文件被编译的时间
__STDC__  //如果编译器遵循ANSI C,其值为1,否则未定义

2.#define

2.1 #define 定义标识符

语法:

#define name stuff

例如以下代码(并且应用了预定义符号):

#include<stdio.h>
#define MAX 100
#define STA static // 为static 这个关键字创建一个简短的名字
//如果定义的stuff过长,可以分成几行写,除了最后一行的每行后面加上一个反斜杠(续行符)
#define DEBUG_PRINT printf("file:%s\nline:%d\n \date:%s\ntime:%s\n",\__FILE__,__LINE__,\__DATE__,__TIME__ )int main()
{DEBUG_PRINT;return 0;
}

运行结果如下:

在这里插入图片描述

这里有一个问题

在define定义标识符的时候,需要加上;吗?

这里建议不要加上;,这样很容易导致问题。

例如:

#define MAX 10;
int main()
{int i = 0;int arr[10] = {0};for(i=0;i<MAX;++i){arr[i] = 1;}return 0;
}

在这种情况下,编译器就会报错,这是由于用#define定义的标识符的使用逻辑是替换,也就是说这里的for语句其实等价于

for(i = 0; i<10;; ++i)
//宏替换之后的结果

很显然,这不符合for语句的语法,因此我们在用#define定义标识符时最好不要加分号,不然有可能会导致错误。

2.2 #define 定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义 宏(define macro)。

宏的声明方式如下:
#define name( parment-list ) stuff
其中的parment-list是一个由逗号隔开的符号表。

注意:
参数列表的左括号必须与name紧邻。
如果两者之间由任何空白存在,参数列表就会被解释为stuff的一部分。

来看下面一段代码:

#define SQUARE(X) x*x
int main()
{printf("%d",SQUARE(5+1));return 0;
}

如果对宏的实现不熟悉的话,可能会觉得这段代码将打印36,事实上将打印11.
为什么呢?

替换文本时,参数x被替换成了5+1,所以这条语句实际上变成了:
printf("%d\n",5+ 1*5 +1);

这样就能知道为什么会出现11的情况了,这是由于替换产生的表达式由于优先级的原因并没有按照预想的次序进行求值。

这时,只需要加括号就可以解决问题。

#define SQUARE(X) ((X)*(X))

从这个例子我们就可以得出一个编程习惯,那就是用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用。也就是说,对于宏来说,我们不能吝啬括号的使用

2.3 #define 替换规则

在程序中扩展#define定义符号和宏时,需要涉及这几个步骤。

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

例如:

#define A 2
#define C A*2
int main()
{printf("%d\n",C);return 0;
}

在这段代码中,C首先将会被替换为A * 2,然后A * 2再被替换成2 * 2

注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

第二点是什么意思呢?看以下代码:

#define A 2
int main()
{printf("this value is A");return 0;
}

这段代码中A并不会被替换,输出的结果仍然是 this value is A, 这就是字符串常量内容不被搜索的意思了。

2.4 #和##

#的作用

如何将参数插入到字符串中呢?

我们来看看这样一段代码:

char* p = "hello ""bit\n"; printf("hello"" bit\n");
printf("%s", p);

通过测试我们会发现,这里输出的都是hello bit。通过这点,我们就发现了字符串是有自动连接的特点的。

#的作用是将一个宏参数编程对应的字符串。

通过这两个特点,我们就可以进行一些骚操作了嘿嘿!

看下面代码:

int i = 10; 
#define PRINT(FORMAT, VALUE)\ 
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
PRINT("%d", i+3);

这里,代码中的#VALUE就会被预处理器处理为:”VALUE“ , 所以这段代码最后会输出
the value of i+3 is 13

##的作用

##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。

这个符号非常的神奇,可以看一下以下代码:

#define ADD_TO_SUM(x,value)\sum##x += value
ADD_TO_SUM(2,10);
//这段代码的意思最后会被预处理器处理为
//sum2 += 10;
//作用是:给sum2这个变量的值增加10.

注意:

这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

2.5 带副作用的宏参数

宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

那么,什么是带有副作用的参数呢?
例如:

x+1 //不带副作用
x++ //带副作用

x++这个代码在对x加一的时候同时也改变了x的值,这就是其副作用。

下面,我们用一段代码来看带有副作用的参数会带来什么问题把。

#define MAX(a,b) ((a)>(b)?(a):(b))
x = 1;
y = 2;
z = MAX(x++,y++);
printf("x=%d y=%d z=%d\n",x,y,z);

如果你把宏和函数搞混在一起的话,可能会觉得结果是x=2,y=3,z=2
但是这题的结果却是x=2,y=4,z=3

问题出在哪呢?
还是那样,宏的原理是替换,所以当前代码经过预处理器处理之后的结果是:

z = ((x++)>(y++)?(x++):(y++));

根据该表达式求出来的值就是上诉结果。

因此,以后使用宏时一定要牢记一点,宏的实现逻辑是替换

2.6 宏和函数的对比

宏通常用于执行简单的计算,例如上面在两数中找出较大值的MAX宏。

另外,宏有时候可以做到函数左不到事,例如:宏的参数可以出现类型,但是函数做不到。

#define MALLOC(num,type)\(type*)malloc(num*sizeof(type))
//使用
int* p = MALLOC(8,char);

宏和函数对比图

属性#define定义宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每 次使用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,相对慢一些,但是区别不会很大
操作符优先级除非加上括号,否则临近的优先级有可能会产生不可预料的后果参数函数只在函数调用的时候求值一次,表达式的求值结果易预测
参数类型宏的参数和类型无关,只要对参数的操作合法,他就可以使用于任何参数类型函数参数与类型有关,如果函数参数类型不同,就需要不同的函数,即使他们执行的任务式相同的。
调试宏不方便调试函数可以逐语句调试
递归宏不能递归函数可以递归

2.7 命名约定

函数和宏的使用语法很相似,为了区分二者,一般写宏有一个习惯:

宏名全部大写
函数名不能全部大写

3.#undef

该指令用于移除一个宏定义

#define NAME A //定义
#undef NAME  //移除定义

4.条件编译

在编译程序的时候我们将一些语句编译或放弃是很简单的。
例如:

调试性的代码,删去过于可惜,保留又会影响输出结果,我们就可以使用条件编译。

#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__ //如果该文件定义了__DEBUG__,则执行下面语句,若没有,则不执行printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif 

4.1 常见的条件编译指令

1. #if 常量表达式 //...
#endif 
//常量表达式由预处理器求值。 
如:
#define __DEBUG__ 1 
#if __DEBUG__ //..
#endif
2.多个分支的条件编译 
#if 常量表达式 //...
#elif 常量表达式 //...
#else //... 
#endif3.判断是否被定义 
#if defined(symbol) 
#ifdef symbol#if !defined(symbol) 
#ifndef symbol4.嵌套指令
#if defined(OS_UNIX) #ifdef OPTION1unix_version_option1(); #endif #ifdef OPTION2 unix_version_option2(); #endif
#elif defined(OS_MSDOS) #ifdef OPTION2msdos_version_option2(); #endif
#endif

5.#error

语法:

#error token-string //标记-字符串

#error指令在编译时发出用户指定的错误消息,然后终止编译

例如:

#ifndef __cplusplus
#error C++ compiler required.
//若程序中没有定义__cplusplus,中止编译并报错
#endif

相关文章:

预处理指令详解

预处理指令详解**1.预定义符号****2.#define****2.1 #define 定义标识符****2.2 #define 定义宏****2.3 #define 替换规则****2.4 #和##****#的作用****##的作用****2.5 带副作用的宏参数****2.6 宏和函数的对比****宏和函数对比图****2.7 命名约定****3.#undef**4.条件编译4.1…...

Redis

一.认识NoSQL 1.SQL 关系型数据库 结构化: 定义主键&#xff0c;无符号型数据等关联的&#xff1a;结构化表和表之间的关系通过外键进行关联&#xff0c;节省存储空间SQL查询&#xff1a;语法固定 SELECT id,name,age FROM tb_user WHERE id1 ACID 2.NoSQL 非关系型数据库 Re…...

Elasticsearch5.5.1 自定义评分插件开发

文本相似度插件开发&#xff0c;本文基于Elasticsearch5.5.1&#xff0c;Kibana5.5.1 下载地址为&#xff1a; Past Releases of Elastic Stack Software | Elastic 本地启动两个服务后&#xff0c;localhost:5601打开Kibana界面&#xff0c;点击devTools&#xff0c;效果图…...

4.4 序列化与反序列化

文章目录1.概述2.特点/应用场景3.涉及到的流对象4.代码实现序列化与反序列化4.1 步骤1&#xff1a;创建学生类Student24.2 步骤2&#xff1a;创建序列化测试类5.测试案例中常见的几种编译错误类型6.为什么反序列化版本号需要与序列化版本号一致&#xff1f;7.自动提示 生成UID …...

647. 回文子串 516. 最长回文子序列

647. 回文子串 方法一&#xff1a;动态规划 dp[i][j]:[i,j]范围的下标字符串s是否为回文子串 遍历字符串&#xff0c;每次判断s[i]与s[j]是否相等 ①若相等&#xff0c;j-i0 即单个字符串s[i]&#xff0c;那么一定为回文子串&#xff0c;赋值为1 ②若相等&#xff0c;j-i1…...

实用小妙招

记录一些实用小妙招&#xff0c;都是收藏夹里收藏的各种文章&#xff0c;总结在一起&#xff0c;持续更新 实用小妙招LinuxUbuntu修改终端语言安装 Node.js (nvm)git 记住账号密码WSL迁移默认用户修改Linux Ubuntu 修改终端语言 apt update apt install -y language-pack-zh…...

别让猴子跳回背上

1.管理者的贡献来自于他们的判断力与影响力&#xff0c;而非他们所投入的个人时间与埋头苦干 2.管理者的绩效表现则是许多人群策群力的结果 3.管理者的时间管理: 老板占用的时间;组织占用的时间;自己占用的时间;外界占用的时间; 4.管理者的策略在于增加自己的时间&#xff0c…...

数据结构 | 线性表

&#x1f525;Go for it!&#x1f525; &#x1f4dd;个人主页&#xff1a;按键难防 &#x1f4eb; 如果文章知识点有错误的地方&#xff0c;请指正&#xff01;和大家一起学习&#xff0c;一起进步&#x1f440; &#x1f4d6;系列专栏&#xff1a;数据结构与算法 &#x1f52…...

Deepwalk深度游走算法

主要思想 Deepwalk是一种将随机游走和word2vec两种算法相结合的图结构数据的挖掘算法。该算法可以学习网络的隐藏信息&#xff0c;能够将图中的节点表示为一个包含潜在信息的向量&#xff0c; Deepwalk算法 该算法主要分为随机游走和生成表示向量两个部分&#xff0c;首先…...

微服务项目【服务调用分布式session共享】

nginx动静分离 第1步&#xff1a;通过SwitchHosts新增二级域名&#xff1a;images.zmall.com 第2步&#xff1a;将本次项目的所有静态资源js/css/images复制到nginx中的html目录下 第3步&#xff1a;在nginx的核心配置文件nginx.conf中新增二级域名images.zmall.com访问映射…...

神经网络的万能逼近定理

这是我见过的讨论神经网络万有逼近问题的最好的文章。在文章中&#xff0c;给出了最清晰&#xff0c;简洁的构造性证明。揭示了它的本质。 三十年前&#xff0c;我们接触到神经网络的万有逼近问题。发表了几篇文章。这些文章把神经网络能力的来历、优点、缺点&#xff0c;都已…...

【信息系统项目管理师】项目管理过程的三万字大论文

【信息系统项目管理师】项目管理过程的三万字大论文 【信息系统项目管理师】项目管理过程的三万字大论文 【信息系统项目管理师】项目管理过程的三万字大论文1.制定项目章程2.识别干系人3.制定范围管理计划4.制定进度管理计划5.制定成本管理计划6.制定质量管理计划7.编制人力资…...

【C++】C++11 ~ 包装器解析

&#x1f308;欢迎来到C专栏~~包装器解析 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&a…...

SpringBoot整合(三)SpringBoot发送邮件

使用SpringBoot发送邮件 邮件发送其实是一个非常常见的需求&#xff0c;用户注册&#xff0c;找回密码等地方&#xff0c;都会用到&#xff0c;Spring Boot 中对于邮件发送&#xff0c;提供了相关的自动化配置类&#xff0c;使得邮件发送变得非常容易。 1、前置工作 目前国内…...

【docker知识】联合文件系统(unionFS)原理

一、说明 Docker CLI 操作起来比较简单——您只需掌握Create、Run、InspPull和Push容器和图像&#xff0c;但是谁想过Docker 背后的内部机制是如何工作的&#xff1f;在这个简单的表象背后隐藏着许多很酷的技术&#xff0c; UnionFS&#xff08;统一文件系统&#xff09;就是其…...

使用Lame库实现wav、pcm转mp3

文章目录 前言 一、Lame库是什么&#xff1f; 二、使用步骤 0.创建native项目 1.下载Lame库 2.pcm转MP3 3.wav转MP3 4、native方法如下 三、注意 总结 前言 因为使用android录音后生成的文件是wav或者pcm格式&#xff0c;项目要求最后的文件需要是mp3格式&#xff0c;于…...

c++11 标准模板(STL)(std::multimap)(三)

定义于头文件 <map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class multimap;(1)namespace pmr { template <class Key, class T…...

【报复性赚钱】2023年5大风口行业

今天就来和大家分享一下&#xff0c;在时代的洪流下&#xff0c;普通人如何顺应大势抓住机遇&#xff01; 实现人在风口上&#xff0c;猪都会飞起来。 根据对市场的观察及各平台数据分析结果&#xff0c;结合国家政策和经济专家的分析&#xff0c;小编预测了2023年将会迎来大…...

单目相机、双目相机和RGB-D相机学习笔记(一些视频和博文网址)

目录1. 单目相机1.1 摄像头原理1.2 单目相机的标定2 双目相机2.1 双目相机定位原理2.2 双目相机的缺陷3 RGB-D相机3.1 深度相机结构光原理3.2 RGB-D相机的应用1. 单目相机 1.1 摄像头原理 视频网址&#xff1a;【全网最详细】摄像头原理分析&#xff08;约25分钟课程&#xf…...

word和wps添加mathtype选项卡

word或wps添加mathtype选项卡 前提 安装好word或wps安装好mathtype 步骤 确认word或wps具体安装位置确认word或wps位数为32位还是64位复制mathtype中的MathPage.wll文件和MathType Commands 2016.dotm文件到STARTUP位置添加受信任位置添加加载项 安装位置 通过开始页面&a…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...