条件编译#if、##ifdef、#ifndef 及#define和const区别
文章目录
1、#ifndef的作用
-
1、头文件中使用,防止头文件被多重调用。
被重复引用是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成。
比如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include “a.h” 和#include "c.h"此时就会造成c.h重复引用。 -
2、作为测试使用,省去注释代码的麻烦。
VS/VC 有两种编译模式,Debug 和 Release。
使用 Debug 模式,这样便于程序的调试;而最终发布的程序,使用 Release 模式,这样编译器会进行很多优化,提高程序运行效率,删除冗余信息。
#include <stdio.h>
#include <stdlib.h>
int main(){
#ifdef _DEBUG
printf("正在使用 Debug 模式编译程序...\n");
#else
printf("正在使用 Release 模式编译程序...\n");
#endif
system("pause");
return 0;
}
- 3、作为不同角色或者场景的判断使用。
如跨平台,在 Windows 和 Linux 下都能运行程序示例
#include <stdio.h>
int main(){
#if _WIN32
printf("This is Windows!\n");
#else
printf("Unknown platform!\n");
#endif
#if __linux__
printf("This is Linux!\n");
#endif
return 0;
}
2、头文件被重复引用会怎么样?
- 影响编译过程效率
对于大工程,头文件重复被引用会导致编译工作量加大,效率低下。 - 引起错误
在头文件中定义了全局变量(虽然这种方式不被推荐,但确实是C规范允许的)这种会引起重复定义。
如下示例,在vc中链接时就出现了变量 i 重复定义的错误,而在c中成功编译。
#ifndef AAA
#define AAA
...
int i;
...
#endif
问题分析:
(1).当你第一个使用这个头的.cpp文件生成.obj的时候,int i 在里面定义了当另外一个使用这个的.cpp再次[单独]生成.obj的时候,int i 又被定义然后两个obj被另外一个.cpp也include 这个头的,连接在一起,就会出现重复定义.
(2).把源程序文件扩展名改成.c后,VC按照C语言的语法对源程序进行编译,而不是C++。在C语言中,若是遇到多个int i,则自动认为其中一个是定义,其他的是声明。
(3).C语言和C++语言连接结果不同,可能(猜测)时在进行编译的时候,C++语言将全局
变量默认为强符号,所以连接出错。C语言则依照是否初始化进行强弱的判断的。(参考)
解决方案:
1).把源程序文件扩展名改成.c。
<x.c>
int i;
(2).推荐解决方案:
.h中只声明 extern int i;在.cpp中定义
<x.h>
#ifndef __X_H__
#define __X_H__
extern int i;
#endif //__X_H__
3、头文件中都要加入#ifndef/#define/#endif ?
- 不是,但是不管怎样,用#ifndef xxx #define xxx #endif或者其他方式避免头文件重复包含,只有好处没有坏处。个人觉得培养一个好的编程习惯是学习编程的一个重要分支。
4、#ifndef/#define/#endif 具体含义
- #ifndef A_H意思是"if not define a.h" 如果不存在a.h
接着的语句应该#define A_H 就引入a.h
最后一句应该写#endif 否则不需要引入
#ifdef identifier
#ifndef identifier
这些指令等效于:
#if defined identifier
#if !defined identifier
5、#ifndef/#define/#endif 示例
#ifndef _STDIO_H_ //如:头文件stdio.h,命名规则一般是头文件名全大写,前后加下划线
#define _STDIO_H_
......
#endif
6、 #if,#ifdef, #ifndef 的比较
- #if 后面跟的是“整型常量表达式”,而 #ifdef 和 #ifndef 后面跟的只能是一个宏名。
- #if 命令要求判断条件为“整型常量表达式”,也就是说,表达式中不能包含变量,而且结果必须是整数;而 if 后面的表达式没有限制,只要符合语法就行
#include <stdio.h>
#define NUM1 10
#define NUM2 20
int main(){
#if (defined NUM1 && defined NUM2)
//代码A
printf("NUM1: %d, NUM2: %d\n", NUM1, NUM2);
#else
//代码B
printf("Error\n");
#endif
return 0;
}
7、const 与 #define的比较
-
宏定义 #define
指令都是以#开始的,我们来看一下简单的宏定义(对象式宏)
#define MAX(x,y) ((x)>(y)?(x):(y))
#define ISLEAP(y) ((y)%4==0&&(y)%100!=0||(y)%400==0)
#define ISSMALL(m) ((m)==4||(m)==6||(m)==9||(m)==11)
#define NORMAL(m) (ISSMALL(m)?30:31)
#define DAYS(y,m) ((m)==2?28+ISLEAP(y):NORMAL(m))
用于调试的示例:
#if (comdition)
{//语句##;}
#endif
//如果(comdition)为真, 也就是逻辑1的话,编译下面的语句,如果(comdition)为假,即逻辑0,则不编译下面的语句。例子如下:
#define DEBUG 1
#if DEBUG
Printf(“Value of i:%d\n”, i);
Printf(“Value of j:%d\n”, j);
#endif
- const 与 #define比较
- 角度1:定义常量:
const 定义的常数是变量 也带类型, #define 定义的只是个常数 不带类型。 - 角度2:空间占用
宏定义不分配内存,变量定义分配内存,
const常量会在内存中分配(可以是堆中也可以是栈中)
#define PI 3.14 //预处理后 占用代码段空间
const float PI=3.14; //本质上还是一个 float,占用数据段空间
- 角度3:起作用的阶段
define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用 - 角度4:起作用的方式
define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误
#define N 2+3 //我们预想的N值是5,我们这样使用N
double a = N/2; //我们预想的a的值是2.5,可实际上a的值是3.5
- 角度5:代码调试
const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉 - 角度6:重定义
const不能重定义,而#define可以通过#undef取消某个符号的定义,再重新定义 - 角度7:防止头文件重复引用
define可以用来防止头文件重复引用 - 角度8:复杂功能
使用define会使得代码看起来非常简单,而const无法实现该功能
例如,MFC在实现六大核心机制中,大量使用了define
1、MFC程序的初始化
2、运行时类型识别(RTTI)
3、动态创建
4、永久保存
5、消息映射
6、消息传递
比如,在实现RTTI功能的时候,定义了如下宏,代码如下:
#define DECLARE_DYNCREATE(class_name) \
DECLARE_DYNAMIC(class_name)\
static CObject* PASCALCreateObject();
上一篇: Swift函数式编程详解
下一篇: #define与typedef的区别!