C/C++条件编译:#ifdef、#else、#endif等
文章目录
- #undef指令
- 从C预处理器角度看已定义
- 条件编译
- 1.#ifdef、#else和#endif指令
- #ifndef指令
- #ifndef指令通常用于防止多次包含一个文件
- 程序使用#ifndef避免文件被重复包含
- #if和#elif指令
- 条件编译还有一个用途是让程序更容易移植
- 参考
程序员可能要为不同的工作环境准备C程序和C库包。不同的环境可能
使用不同的代码类型。预处理器提供一些指令,程序员通过修改#define的值
即可生成可移植的代码。#undef指令取消之前的#define定义。#if、#ifdef、
#ifndef、#else、#elif和#endif指令用于指定什么情况下编写哪些代码。#line
指令用于重置行和文件信息,#error指令用于给出错误消息,#pragma指令用
于向编译器发出指令。
#undef指令
#undef指令用于“取消”已定义的#define指令。也就是说,假设有如下定义:
#define LIMIT 400
然后,下面的指令:
#undef LIMIT
将移除上面的定义。现在就可以把LIMIT重新定义为一个新值。即使原
来没有定义LIMIT,取消LIMIT的定义仍然有效。如果想使用一个名称,又
不确定之前是否已经用过,为安全起见,可以用#undef 指令取消该名字的定
义。
从C预处理器角度看已定义
处理器在识别标识符时,遵循与C相同的规则:标识符可以由大写字
母、小写字母、数字和下划线字符组成,且首字符不能是数字。当预处理器
在预处理器指令中发现一个标识符时,它会把该标识符当作已定义的或未定
义的。这里的已定义表示由预处理器定义。如果标识符是同一个文件中由前
面的#define指令创建的宏名,而且没有用#undef 指令关闭,那么该标识符是
已定义的。如果标识符不是宏,假设是一个文件作用域的C变量,那么该标
识符对预处理器而言就是未定义的。
已定义宏可以是对象宏,包括空宏或类函数宏:
#define LIMIT 1000 // LIMIT是已定义的
#define GOOD // GOOD 是已定义的
#define A(X) ((-(X))*(X)) // A 是已定义的
int q; // q 不是宏,因此是未定义的
#undef GOOD // GOOD 取消定义,是未定义的
注意,#define宏的作用域从它在文件中的声明处开始,直到用#undef指
令取消宏为止,或延伸至文件尾(以二者中先满足的条件作为宏作用域的结
束)。
另外还要注意,如果宏通过头文件引入,那么#define在文件中的位置
取决于#include指令的位置。
稍后将介绍几个预定义宏,如__DATE__和__FILE__。这些宏一定是已
定义的,而且不能取消定义。
条件编译
可以使用其他指令创建条件编译(conditinal compilation)。也就是说,
可以使用这些指令告诉编译器根据编译时的条件执行或忽略信息(或代码)
块。
1.#ifdef、#else和#endif指令
我们用一个简短的示例来演示条件编译的情况。考虑下面的代码:
#ifdef MAVIS#include "horse.h"// 如果已经用#define定义了 MAVIS,则执行下面的指令#define STABLES 5
#else#include "cow.h" //如果没有用#define定义 MAVIS,则执行下面的指令#define STABLES 15
#endif
这里使用的较新的编译器和 ANSI 标准支持的缩进格式。如果使用旧的
编译器,必须左对齐所有的指令或至少左对齐#号,如下所示:
#ifdef MAVIS
# include "horse.h" // 如果已经用#define定义了 MAVIS,则执行下面的指令
# define STABLES 5
#else
# include "cow.h" //如果没有用#define定义 MAVIS,则执行下面的指令
# define STABLES 15
#endif
#ifdef指令说明,如果预处理器已定义了后面的标识符(MAVIS),则
执行#else或#endif指令之前的所有指令并编译所有C代码(先出现哪个指令
就执行到哪里)。如果预处理器未定义MAVIS,且有 #else指令,则执行
#else和#endif指令之间的所有代码。
#ifdef #else很像C的if else。两者的主要区别是,预处理器不识别用于标
记块的花括号({}),因此它使用#else(如果需要)和#endif(必须存在)
来标记指令块。这些指令结构可以嵌套。也可以用这些指令标记C语句块
ifdef.c程序
/* ifdef.c -- 使用条件编译 */
#include <stdio.h>
#define JUST_CHECKING
#define LIMIT 4
int main(void){int i;int total = 0;for (i = 1; i <= LIMIT; i++){total += 2 * i*i + 1;#ifdef JUST_CHECKINGprintf("i=%d, running total = %d\n", i, total);#endif}printf("Grand total = %d\n", total);return 0;
}
编译并运行该程序后,输出如下:
i=1, running total = 3
i=2, running total = 12
i=3, running total = 31
i=4, running total = 64
Grand total = 64
如果省略JUST_CHECKING定义(把它放在C注释中,或者使用#undef指
令取消它的定义)并重新编译该程序,只会输出最后一行。可以用这种方法
在调试程序。
定义JUST_CHECKING并合理使用#ifdef,编译器将执行用于
调试的程序代码,打印中间值。调试结束后,可移除JUST_CHECKING定义
并重新编译。如果以后还需要使用这些信息,重新插入定义即可。这样做省
去了再次输入额外打印语句的麻烦。
#ifdef还可用于根据不同的C实现选择合适的代码块。
#ifndef指令
#ifndef指令与#ifdef指令的用法类似,也可以和#else、#endif一起使用,
但是它们的逻辑相反。#ifndef指令判断后面的标识符是否是未定义的,常用
于定义之前未定义的常量。如下所示:
/* arrays.h */
#ifndef SIZE
#define SIZE 100
#endif
(旧的实现可能不允许使用缩进的#define)
通常,包含多个头文件时,其中的文件可能包含了相同宏定义。#ifndef
指令可以防止相同的宏被重复定义。在首次定义一个宏的头文件中用#ifndef
指令激活定义,随后在其他头文件中的定义都被忽略。
#ifndef指令还有另一种用法。假设有上面的arrays.h头文件,然后把下面
一行代码放入一个头文件中:
#include "arrays.h"
SIZE被定义为100。
但是,如果把下面的代码放入该头文件:
#define SIZE 10
#include "arrays.h"
SIZE则被设置为10。这里,当执行到#include "arrays.h"这行,处理
array.h中的代码时,由于SIZE是已定义的,所以跳过了#define SIZE 100这行
代码。鉴于此,可以利用这种方法,用一个较小的数组测试程序。测试完毕
后,移除#define SIZE 10并重新编译。这样,就不用修改头文件数组本身
了。
#ifndef指令通常用于防止多次包含一个文件
#ifndef指令通常用于防止多次包含一个文件。也就是说,应该像下面这
样设置头文件:
/* things.h */
#ifndef THINGS_H_
#define THINGS_H_
/* 省略了头文件中的其他内容*/
#endif
假设该文件被包含了多次。当预处理器首次发现该文件被包含时,
THINGS_H_是未定义的,所以定义了THINGS_H_,并接着处理该文件的其
他部分。当预处理器第2次发现该文件被包含时,THINGS_H_是已定义的,
所以预处理器跳过了该文件的其他部分。
为何要多次包含一个文件?
最常见的原因是,许多被包含的文件中都包含着其他文件,所以显式包含的文
件中可能包含着已经包含的其他文件。
这有什么问题?
在被包含的文件中有某些项(如,一些结构类型的声明)只能
在一个文件中出现一次。C标准头文件使用#ifndef技巧避免重复包含。
但是,这存在一个问题:
如何确保待测试的标识符没有在别处定义。通常使用这些方法
解决这个问题:
用文件名作为标识符、使用大写字母、用下划线字符代替文件名中的点字符、
用下划线字符做前缀或后缀(可能使用两条下划线)。
例如,查看stdio.h头文件,可以发现许多类似的代码:
#ifndef _STDIO_H
#define _STDIO_H
// 省略了文件的内容
#endif
你也可以这样做。但是,由于系统标准库使用下划线作为前缀,所以在自
己的代码中不要这样写,避免与标准头文件中的宏发生冲突。
程序使用#ifndef避免文件被重复包含
names.c程序
// names.h --修订后的 names_st 头文件,避免重复包含
#ifndef NAMES_H_
#define NAMES_H_
// 明示常量
#define SLEN 32
// 结构声明
struct names_st
{char first[SLEN];char last[SLEN];
};
// 类型定义
typedef struct names_st names;
// 函数原型
void get_names(names *);
void show_names(const names *);
char * s_gets(char * st, int n);
#endif
下面程序测试该头文件没问题,但是如果把#ifndef保护删除后,程序就无法通过编译。
doubincl.c程序:
// doubincl.c -- 包含头文件两次
#include <stdio.h>
#include "names.h"
#include "names.h" // 不小心第2次包含头文件
int main(){names winner = { "Less", "Ismoor" };printf("The winner is %s %s.\n", winner.first,winner.last);return 0;
}
#if和#elif指令
#if指令很像C语言中的if。#if后面跟整型常量表达式,如果表达式为非
零,则表达式为真。可以在指令中使用C的关系运算符和逻辑运算符:
#if SYS == 1
#include "ibm.h"
#endif
可以按照if else的形式使用#elif(早期的实现不支持#elif)。例如,可
以这样写:
#if SYS == 1
#include "ibmpc.h"
#elif SYS == 2
#include "vax.h"
#elif SYS == 3
#include "mac.h"
#else
#include "general.h"
#endif
较新的编译器提供另一种方法测试名称是否已定义,即用
#if defined(VAX)
代替
#ifdef VAX
这里,defined是一个预处理运算符,如果它的参数是用#defined定义
过,则返回1;否则返回0。
这种新方法的优点是,它可以和#elif一起使用。
下面用这种形式重写前面的示例:
#if defined (IBMPC)
#include "ibmpc.h"
#elif defined (VAX)
#include "vax.h"
#elif defined (MAC)
#include "mac.h"
#else
#include "general.h"
#endif
如果在VAX机上运行这几行代码,那么应该在文件前面用下面的代码定
义VAX:
#define VAX
条件编译还有一个用途是让程序更容易移植
条件编译还有一个用途是让程序更容易移植。改变文件开头部分的几个
关键的定义,即可根据不同的系统设置不同的值和包含不同的文件。
参考
《C Primer Plus》
相关文章:
C/C++条件编译:#ifdef、#else、#endif等
文章目录 #undef指令从C预处理器角度看已定义条件编译1.#ifdef、#else和#endif指令 #ifndef指令#ifndef指令通常用于防止多次包含一个文件程序使用#ifndef避免文件被重复包含 #if和#elif指令条件编译还有一个用途是让程序更容易移植 参考 程序员可能要为不同的工作环境准备C程序…...
基于51单片机步进电机节拍步数正反转LCD1602显示( proteus仿真+程序+原理图+设计报告+讲解视频)
基于51单片机步进电机节拍步数正反转LCD1602显示 📑1. 主要功能:📑2. 讲解视频:📑3. 仿真📑4. 程序代码📑5. 设计报告📑6. 设计资料内容清单&&下载链接📑[资料下…...
Vim 从何而来?
Vim 编辑器的创造者、维护者和终身领导者 Bram Moolenaar 为了纪念这位杰出的荷兰程序员,我们今天来聊一聊 Vim 的历史。 Vim 无处不在。它被很多人使用。同时 Vim 可能是世界上 “最难用的软件之一” ,但是又多次被程序员们评价为 最受欢迎的 代码编辑…...
Auto.js 清除指定应用缓存
本文所有教程及源码、软件仅为技术研究。不涉及计算机信息系统功能的删除、修改、增加、干扰,更不会影响计算机信息系统的正常运行。不得将代码用于非法用途,如侵立删!Auto.js 清除指定应用缓存 环境 win10Pixel4Android13var packageName = ""; // 包名 var resu…...
[EFI]Surface Pro 4电脑 Hackintosh 黑苹果引导文件
硬件型号驱动情况主板Surface Pro 4处理器Intel Core i5-6300U 2.5GHz已驱动内存16GB DDR4 2400Mhz已驱动硬盘Samsung SSD 860 EVO 250G Media (Install on SSD External)已驱动显卡Intel HD Graphics 520 2GBmacOS 13以上自行添加显卡补丁声卡Realtek ALC3269(id 3…...
【Java 进阶篇】深入浅出:JQuery 事件绑定的奇妙世界
在前端的世界里,事件是不可或缺的一部分。用户的点击、输入、滚动等行为都触发着各种事件,而如何在代码中捕捉并处理这些事件是每位前端开发者必须掌握的技能之一。本文将带你深入浅出,探索 JQuery 中的事件绑定,为你揭开这个奇妙…...
Pair用法示例:
这里用到了 org.apache.commons.lang3.tuple.Pair 来封装数据(就是不想自己再写一个 DO 或者 VO 或者 MO) 在Java中,Pair是一种简单的数据结构,用于存储两个相关联的值。它没有特定的内置类,但可以通过自定义实现或使…...
rpc依赖安装
依赖: 0、boost:用于实现多线程等; 1、protobuf:用于实现数据的序列化、反序列化,也用于定义和生成rpc数据及接口; 2、libevent:用于实现基于IO多路复用机制的网络事件循环。 其实可以直接用包…...
文件存储服务 实时通信服务 HTTP通信协议
目录 文件存储服务实时通信服务HTTP通信协议 👍 点赞,你的认可是我创作的动力! ⭐️ 收藏,你的青睐是我努力的方向! ✏️ 评论,你的意见是我进步的财富! 文件存储服务 文件存储服务是一种用于…...
Redis - 订阅发布替换 Etcd 解决方案
为了减轻项目的中间件臃肿,由于我们项目本身就应用了 Redis,正好 Redis 的也具备订阅发布监听的特性,正好应对 Etcd 的功能,所以本次给大家讲解如何使用 Redis 消息订阅发布来替代 Etcd 的解决方案。接下来,我们先看 R…...
Hessian协议详解
前言 Hessian协议是一种基于二进制的轻量级远程调用协议,用于在分布式系统中进行跨语言的通信。它使用简单的二进制格式来序列化和反序列化数据,并支持多种编程语言,如Java、C#、Python等。Hessian协议相对于其他协议的优势在于其简单性和高…...
【AI视野·今日Sound 声学论文速览 第三十六期】Mon, 30 Oct 2023
AI视野今日CS.Sound 声学论文速览 Mon, 30 Oct 2023 Totally 7 papers 👉上期速览✈更多精彩请移步主页 Daily Sound Papers Style Description based Text-to-Speech with Conditional Prosodic Layer Normalization based Diffusion GAN Authors Neeraj Kumar, A…...
Android Jetpack的组件介绍,常见组件解析
jetpack组件有哪些 Android Jetpack是一个集成Android应用程序组件的一站式解决方案。它使开发人员能够专注于他们的应用程序的真正创新部分,而不会受到Android平台特定的限制。Jetpack组件可分为四个类别: 架构组件(Architecture Componen…...
ImportError: cannot import name ‘url_quote‘ from...
👨🏻💻 热爱摄影的程序员 👨🏻🎨 喜欢编码的设计师 🧕🏻 擅长设计的剪辑师 🧑🏻🏫 一位高冷无情的编码爱好者 大家好,我是全栈工…...
一文看分布式锁
为什么会存在分布式锁? 经典场景-扣库存,多人去同时购买一件商品,首先会查询判断是否有剩余,如果有进行购买并扣减库存,没有提示库存不足。假如现在仅存有一件商品,3人同时购买,三个线程同时执…...
Jenkins自动化部署一个Maven项目
Jenkins自动化部署 提示:本教程基于CentOS Linux 7系统下进行 Jenkins的安装 1. 下载安装jdk11 官网下载地址:https://www.oracle.com/cn/java/technologies/javase/jdk11-archive-downloads.html 本文档教程选择的是jdk-11.0.20_linux-x64_bin.tar.g…...
K8S1.23.5部署(此前1.17版本步骤囊括)及问题记录
应版本需求,升级容器版本为1.23.5 kubernetes组件 一个kubernetes集群主要由控制节点(master)与工作节点(node)组成,每个节点上需要安装不同的组件。 master控制节点:负责整个集群的管理。 …...
基于java web的中小型人力资源管理系统
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...
Python学习笔记--Python关键字yield
原文:http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained 注:这是一篇 stackoverflow 上一个火爆帖子的译文 问题 Python 关键字 yield 的作用是什么?用来干什么的? 比如,我正在试图理解下面的代码: def node._get_child_candidates(self,…...
CF 850 C Arpa and a game with Mojtaba(爆搜优化SG)
CF 850 C. Arpa and a game with Mojtaba(爆搜优化SG) Problem - C - Codeforces Arpa and a game with Mojtaba - 洛谷 思路:显然对于每一种质因子来说操作都是独立的 , 因此可以考虑对于每一种质因子求当前质因子的SG &#…...
OpenGL之标准化设备坐标(Normalized Device Coordinate =NDC)
NDC坐标本质是比例1. 设备无关性NDC 使图形渲染与屏幕分辨率无关。无论屏幕是 1280720 还是 19201080,同样的 NDC 坐标都会渲染出相同比例的图形(图形会随着分辨率变化而自动拉伸),表现如下:┌────────────…...
新书推荐:《尊严的颓败》在废墟之上,寻找灵魂的微光
当世界沦为巨大的名利场,当人被简化为数据与欲望的载体,我们该如何定义“人”?又该如何安放那颗被称为“灵魂”的种子?洛本的《尊严的颓败》并非一本让人阅读时感到轻松愉悦的书,它更像是一把手术刀,精准地…...
AutoUnipus:重新定义U校园学习效率的智能解决方案
AutoUnipus:重新定义U校园学习效率的智能解决方案 【免费下载链接】AutoUnipus U校园脚本,支持全自动答题,百分百正确 2024最新版 项目地址: https://gitcode.com/gh_mirrors/au/AutoUnipus 还在为U校园平台上堆积如山的网课任务而焦虑吗?每天花费…...
LeetCode 2946. 循环移位后的矩阵相似检查【数学周期性+原地比较】简单
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...
临近起飞,在哪个平台更容易捡漏特价机票?2026年实测指南
“机票越临近起飞越便宜”——这个说法你一定听过。每逢假期临近,总有人在社交媒体上分享自己“起飞前两小时抢到白菜价机票”的神奇经历。但当你真的想在清明、五一出行前“赌一把”时,往往发现价格不仅没降,反而翻倍了。那么问题来了&#…...
OpenClaw自动化测试:百川2-13B驱动的前端元素定位与交互验证
OpenClaw自动化测试:百川2-13B驱动的前端元素定位与交互验证 1. 从手工测试到智能测试的进化之路 作为一名长期奋战在前端测试一线的开发者,我经历过从纯手工点击到Selenium脚本,再到Playwright框架的技术迭代。每次升级都带来效率提升&…...
把Camunda流程引擎当SaaS用?多租户与外部任务实战指南(基于RuoYi改造)
基于Camunda构建企业级流程中心的架构设计与实战 在数字化转型浪潮中,业务流程自动化已成为企业提升运营效率的核心手段。当一家企业同时运行CRM、OA、ERP等多个业务系统时,每个系统都需要工作流支持,但为每个系统单独部署和维护Camunda引擎显…...
双摆控制系统:LQR、LQG、LQI控制器及龙伯格观测器文件清单
移动小车上双摆的LQR、LQG、LQI控制器和龙伯格观测器文件列表: LQG.m LQG_non_linear.m LQI.m LQR.m LQR_Non_linear.m Luenberger_observer.m Observer_non_linear.m 最近蹲在实验室的工位上啃移动小车双摆的控制代码,翻来覆去调了快两周,终…...
MCP3202 12位SPI ADC驱动开发与嵌入式工程实践
1. MCP3202 12位串行ADC嵌入式驱动深度解析与工程实践1.1 芯片特性与系统定位MCP3202 是 Microchip 推出的低功耗、逐次逼近型(SAR)12位模数转换器,专为嵌入式系统中高精度模拟信号采集场景设计。其核心电气特性如下:参数规格工程…...
OpCore-Simplify:让黑苹果配置从复杂到简单的智能化革命
OpCore-Simplify:让黑苹果配置从复杂到简单的智能化革命 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 你是否曾为黑苹果(Hac…...
