1667 字
8 分钟
宏定义与条件编译
2023-09-17

这里涉及到了#ifndef#define#endif,我们先来回忆一下它们三个有关的知识

1.预处理指令#

常用的预处理指令有三种:

  • ==宏定义==指令:#define
  • ==文件包含==指令:#include
  • ==条件编译==指令:#if、#elif、#else、#ifdef、#ifndef、#endif

这里我们用到的#ifndef虽然是宏定义的一种,但其实更确切的说应该是属于条件编译

2.条件编译是什么#

条件编译是指==预处理器==根据相关的==条件编译指令==,选择性将源代码中的==部分==代码送到==编译器==进行编译。

我们来设想一下,如果我们有一套代码,既能运行在windows上,也能运行在linux上,那么这套代码的自定义头文件中,就应该包含适用于两套操作系统的头文件。

当我们把这套代码运行在linux上时,如果直接全都编译了,有一部分适用于windows的头文件这个时候就是无用的代码,只会==增加内存开销==,==降低程序的效率==

  • 性能是对硬件说的,指的是硬件(如CPU,显卡)的运算能力强还是弱。这个理解起来很简单就不多说。

  • 效率是对软件说的,指的是软件(如操作系统,应用程序软件)的算法以及编程质量的好坏,一般也叫执行效率,比如说我们2个人编程,计算公式N(N-1)(N-2)的值,但是两个人编写的程序不是完全一样的,因此执行同一运算的时间也有所差别,时间短的我们一般都会认为更好了(事实上未必),这就是效率。

3.条件编译的作用#

条件编译就是用来应对这种情况的,条件编译的两个作用:

  • ==提高程序的灵活性和可移植性==:使用条件编译,可以按照条件,==选择性==省略一部分无用代码,生成不同的目标文件
  • ==避免头文件重复引用的问题(重要)==:头文件可能存在==相互包含==的关系,如果不预先处理,编译器会报错

4.条件编译指令以及对应的含义#

条件编译指令有如下几个:

指令含义
#if如果满足条件,执行这部分的代码
#elif【否则如果含义】,如果上面的条件不满足且 #elif 的条件,才执行这部分代码
#else【否则含义】,如果上面的条件不满足,执行这部分代码
#ifdef如果已经定义了宏,执行这部分代码
#ifndef如果没有定义宏,执行这部分代码
#endif条件编译结束指令

5.条件编译指令的使用格式#

1.常规使用格式#if-#elif-#else-#endif#

#include <stdio.h>
#define AA 0
#define BB 1
int main() {
#if AA
printf("1");
#elif BB
printf("2");
#else
printf("3");
#endif
return 0;
}
// 运行结果:2

这种格式有点类似于判断结构中的 if-else 条件语句,但又有着本质的区别。

一个是==预处理指令==,一个是==编译语句==,==执行的阶段不同==,编译器==要处理的代码量也不一样==。

2.删除已定义的宏#ifdef-#endif#

#include <stdio.h>
#define AA 123;
#ifdef AA
#undef AA
#endif
int main() {
int AA = 33;
printf("%d", AA);
return 0;
}
// 运行结果:33

#ifdef 是用来判断程序中是否已经定义了宏。如果已经定义了,那么就==执行 #ifdef 到 #endif 之间的内容==;没有定义,那么==跳过这个内容==。

例如:我想在下面的代码中使用 AA 这个标识符,但我==不确定==前面的内容(引用的头文件里面)中==是否已经定义过==标识符为 AA 的宏。

那我就可以用这个方法进行判断,如果已经定义了 AA 这个宏,那就删除这个宏名,解放出标识符。

3.检测头文件是否被重复引用#ifndef-#endif#

#include <stdio.h>
#ifndef PI
#define PI 3.1415
#endif
int main() {
printf("%f", PI);
return 0;
}
// 运行结果:3.1415

#ifndef 用来判断,程序中是否==没有定义==名字为某个标识符的宏。

如果没有,那就执行 #ifndef 到 #endif 之间的内容;

如果已经定义了,那么就会跳过这个部分的内容。

这个方法最常用在==头文件==中,用来检测==头文件是否被重复引用==的问题。

头文件重复引的例子:#

1.头文件 h1.h 的代码如下:

h1.h
#include "h2.h"

2.头文件 h2.h 的代码如下:

h2.h
#include "h1.h"

3.主体代码文件 main.cpp 的代码如下:

#include "h1.h"
#include "h2.h"
int main() {
return 0;
}
// 运行结果:报错,头文件重复包含

解决办法#

在相关的头文件里面,添加#ifndef-#endif条件编译,就可以避免头文件重复包含这种问题

新的 h1.h 代码如下:

h1.h
#ifndef _H1_H_
#define _H1_H_
#include "h2.h"
#endif

新的 h2.h 代码如下:

h2.h
#ifndef _H2_H_
#define _H2_H_
#include "h1.h"
#endif

注意要写#endif#

所有的==条件编译==格式,最后都要有一个#endif,这个指令表示==条件编译已经结束了==,如果没有这个指令,就会报错

6.条件编译与条件语句的区别#

1.运行阶段#

  • 条件编译:属于==预处理指令==, 而预处理指令相当于是==文本替换==的作用,在==预处理阶段==,条件编译就将==满足条件的代码==送到==编译器==进行编译
  • 条件语句:属于==语句==,是在编译后,==程序运行时==才开始执行判断
生效的阶段效果
条件编译预处理阶段将满足条件的代码送到编译器进行编译
条件语句程序运行时执行判断

2.编译的代码量#

条件编译与条件语句要编译的代码量是不同的,

  • 条件编译:只会将满足条件的部分代码送入编译器,而不满足条件的代码会被清理掉
  • 条件语句:全部代码都要进入编译器
1要编译的代码量
条件编译满足条件的部分代码,不满足条件的代码会被清理掉
条件语句全部代码

假设:有2000行代码,其中1000行与windows有关,1000行与linux有关,当我们把这些代码运行到linux上时,只有1000行与linux有关的代码是有效的,

如果使用条件语句,这2000行代码都会进入编译器进行编译,然后再进行判断;

如果使用条件编译,只会有1000行与linux有关的代码被送到编译器进行编译,可以==节省编译时间==,==减少内存开销==

宏定义与条件编译
https://fuwari.cbba.top/posts/宏定义与条件编译/
作者
Chen_Feng
发布于
2023-09-17
许可协议
CC BY-NC-SA 4.0