目录
什么是预处理?
一、情况1
二、情况2
三、情况3
四、【#if—#elif】
五、文件中的 #ifndef
六、#ifndef精华汇总
什么是预处理?
预处理就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译。这样做的好处是,经过处理后的代码,将会变的很精短。
关于预处理命令中的文件包含(#include),宏定义(#define)。这里主要是对条件编译(#ifdef,#else,#endif,#if等)进行说明。以下分3种情况:
一、情况1
#ifdef _XXXX
...程序段1...
#else...程序段2...
#endif
这表明如果标识符_XXXX已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。
与之对比:
#ifndef _XXXX
...程序段1...
#else
...程序段2...
#endif
这里使用了#ifndef,表示的是if not def。当然是和#ifdef相反的状况(如果没有定义了标识符_XXXX,那么执行程序段1,否则执行程序段2)。
#ifndef
...程序段1...
#else...程序段2...
#endif
例如:
#define NUM
.............
.............
#ifdef NUM
printf("之前NUM有过定义啦!:) \n");
#elseprintf("之前NUM没有过定义!:( \n");
#endif
如果程序开头有#define NUM这行,即NUM有定义,碰到下面#ifdef NUM的时候,当然执行第一个printf。否则第二个printf将被执行。用这种,可以很方便的开启/关闭整个程序的某项特定功能。
根据上述情况,我们这次采用c语言的条件编译指令。它的格式是这样的:
简单解释一下就是如果已经用#define定义了tcp_c,则执行TCP_Client这个程序,否则执行TCP_Server这个程序。
二、情况2
#ifndef _XXXX
...程序段1...
#else
...程序段2...
#endif
这里使用了#ifndef,表示的是if not def。当然是和#ifdef相反的状况(如果没有定义了标识符_XXXX,那么执行程序段1,否则执行程序段2)。
三、情况3
#if 常量
...程序段1...
#else
...程序段2...
#endif这里表示,如果常量为真(非0,随便什么数字,只要不是0),就执行程序段1,否则执行程序段2。
这种方法可以将测试代码加进来。当需要开启测试的时候,只要将常量变1就好了。而不要测试的时候,只要将常量变0。
四、【#if—#elif】
#if更加灵活一些,后面可以跟常量表达式,如果表达式为非零值,则表达式为真,否则为假。故可以使用关系运算符和逻辑运算符。
如果我想在UDP、TCP_Server和TCP_Client三个函数中进行切换,我们可以用上面的语句:
#if #elif #endif
如果工程较大,分散度高,可以使用这种方法,效果是一样的。
#if #endif
五、文件中的 #ifndef
头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
将头文件的内容都放在#ifndef和#endif中。不管头文件会不会被多个文件引用,都要加上这个。
#ifndef <标识>
#define<标识>......
......#endif
<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef _STDIO_H_//先测试x是否被宏定义过(一般来说指定是没有定义过)
#define _STDIO_H_ //如果x没有被宏定义过,定义x,并编译下述程序......
#endif //终止if
在#ifndef中定义变量出现的问题(一般不定义在#ifndef中)。
#ifndef AAA
#define AAA
...
int i;
...
#endif
.h中只声明 extern int i;在.cpp中定义
#ifndef __X_H__
#define __X_H__
extern int i;
#endif //__X_H__
变量一般不要定义在.h文件中!!!!!
#ifndef GRAPHICS_H // 防止graphics.h被重复引用
#define GRAPHICS_H
#include // 引用标准库的头文件
…
#include “myheader.h” // 引用非标准库的头文件
…
void Function1(…); // 全局函数声明
…
class Box // 类结构声明
{
…
};
#endif
六、#ifndef精华汇总
条件指示符最主要的目的是防止头文件的重复包含和编译。
语句1 #ifndef 标识1 //判断标识1是否定义,如果定义则返回假,如果没有定义则返回真。
语句2 #define 标识1 //如果标识1没有被定义,则重定义标识1。即执行语句2。
语句3 #endif 如果标识1已经被定义,则直接跳过语句2、语句3。直接执行语句4、语句5。
语句4 ......
语句5 ......
备注:#ifndef 和 #endif 要一起使用,如果丢失#endif,可能会报错。
需要注意的是,#ifndef起到的效果是防止一个源文件两次包含同一个头文件,而不是防止两个源文件包含同一个头文件。网上很多资料对这一细节的描述都是错误的。事实上,防止同一头文件被两个不同的源文件包含这种要求本身就是不合理的,头文件存在的价值就是被不同的源文件包含。
例如要编写头文件test.h,在头文件开头写上两行:
#ifndef _TEST_H
#define _TEST_H //一般是文件名的大写
头文件结尾写上一行:
#endif
这样一个工程文件里同时包含两个test.h时,就不会出现重定义的错误了。
当第一次包含test.h时,由于没有定义_TEST_H,条件为真,这样就会包含(执行)#ifndef _TEST_H和#endif之间的代码,当第二次包含test.h时前面一次已经定义了_TEST_H,条件为假,#ifndef _TEST_H和#endif之间的代码也就不会再次被包含,这样就避免了重定义了。
而把头文件的内容都放在#ifndef和#endif中,则无论头文件会不会被多个文件引用,都需要加上这个。一般格式是这样的:
#ifndef <标识>
#define <标识>
......
......
#endif