预处理就是在进行编译的第一遍词法扫描和语法分析之前所作的工作。说白了,就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译。这样做的好处是,经过处理后的代码,将会变的很精短。
   关于预处理命令中的文件包含(#i nclude),宏定义(#define),书上已经有了详细的说明,在这里就不详述了。这里主要是对条件编译(#ifdef,#else,#endif,#if等)进行说明。以下分3种情况:

1:情况1:
#ifdef _XXXX
...程序段1...
#else
...程序段2...
#endif
   这表明如果标识符_XXXX已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。

例: 
#define NUM
.............
.............
.............
#ifdef NUM
   printf("之前NUM有过定义啦!:) \n");
#else
   printf("之前NUM没有过定义!:( \n");
#endif
}

如果程序开头有#define NUM这行,即NUM有定义,碰到下面#ifdef NUM的时候,当然执行第一个printf。否则第二个printf将被执行。
   我认为,用这种,可以很方便的开启/关闭整个程序的某项特定功能。

2:情况2: 
#ifndef _XXXX 
...程序段1... 
#else 
...程序段2... 
#endif
   这里使用了#ifndef,表示的是if not def。当然是和#ifdef相反的状况(如果没有定义了标识符_XXXX,那么执行程序段1,否则执行程序段2)。

3:情况3:
#if 常量 
...程序段1...
#else
...程序段2...
#endif 
   这里表示,如果常量为真(非0,随便什么数字,只要不是0),就执行程序段1,否则执行程序段2。
   我认为,这种方法可以将测试代码加进来。当需要开启测试的时候,只要将常量变1就好了。而不要测试的时候,只要将常量变0。

# ifdef   #ifndef 等用法

 
文件中的#ifndef

头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。

还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:

#ifndef <标识> 
#define <标识>

...... 
......

#endif

<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h

#ifndef _STDIO_H_ 
#define _STDIO_H_

......

#endif

2.在#ifndef中定义变量出现的问题(一般不定义在#ifndef中)。

#ifndef   AAA
#define AAA
...
int i;
...
#endif
里面有一个变量定义
在vc中链接时就出现了i重复定义的错误,而在c中成功编译。

结论:

(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。

(2).推荐解决方案:
.h中只声明 extern int i;在.cpp中定义

<x.h>
#ifndef __X_H__
#define __X_H__
extern int i;
#endif //__X_H__
<x.c>
int i;

注意问题:

(1).变量一般不要定义在.h文件中。

ifndef/define/endif的用法与实例分析

用法:

.h文件,如下:
#ifndef XX_H
#define XX_H
……
#endif

这样如果有两个地方都包含这个头文件,就不会出现两次包含的情况,因为在第二次包含时XX_H已经有定义了,所以就不再 include了。
-----------------------------------------------------------------------------------------------------------------------------------
#ifndef GRAPHICS_H     // 防止graphics.h被重复引用 
#define GRAPHICS_H

#include <math.h>      // 引用标准库的头文件 
… 
#include “myheader.h” // 引用非标准库的头文件 
… 
void Function1(…);         // 全局函数声明 
… 
class Box                       // 类结构声明 

… 
}; 
#endif
-----------------------------------------------------------------------------------------------------------------------------------
假设你的工程里面有4个文件,分别是a.cpp,b.h,c.h,d.h 
a.cpp的头部是:
#include "b.h " 
#include "c.h "

b.h和c.h的头部都是: 
#include "d.h "

而d.h里面有class D的定义。

这样一来, 
编译器编译a.cpp的时候,先根据#include "b.h "去编译b.h这个问题,再根据b.h里面的#include "d.h ",去编译d.h的这个文件,这样就把d.h里面的class D编译了;然后再根据a.cpp的第二句#include "c.h ",去编译c.h,最终还是会找到的d.h里面的class D,但是class D之前已经编译过了,所以就会报重定义错误。

加上ifndef/define/endif,就可以防止这种重定义错误。
-----------------------------------------------------------------------------------------------------------------------------------

1.比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。 还是把头文件的内容都放在#ifndef和#endif中吧。 
不管你的头文件会不会被多个文件引用,你都要加上这个。 
一般格式是这样的: 
#ifndef <标识> 
#define <标识> 
...... 
...... 
#endif <标识> 
在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h 
#ifndef _STDIO_H_ 
#define _STDIO_H_ 
...... 
#endif

2.在#ifndef中定义变量出现的问题(一般不定义在#ifndef中)。 
#ifndef AAA 
#define AAA 
... 
int i; 
... 
#endif 
里面有一个变量定义在vc中链接时就出现了i重复定义的错误,而在c中成功编译。 
原因: 
(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。 
(2).推荐解决方案:.h中只声明 extern int i; 
在.cpp中定义 
#ifndef __X_H__ 
#define __X_H__ 
extern int i; 
#endif //__X_H__ int i; 
注意问题:变量一般不要定义在.h文件中。

接下来 iOS 实际运用:

yykit 是最近一个很火的开源框架,今天来研究看看yykit源码和一些原理,刚拿到yykit还是 很惊讶,来看看yykit 的目录,功能不仅强大,而且是十分全面的,我们来看看他的目录结构

EA102D35-DD95-4EFA-8098-325B855E8CE7.png

不得不惊叹,真的好全面,一篇文章根本看不完,今天还是先从base 说起吧,

09ECF4C2-6F37-40F8-BCBB-EEAD1AE9C19F.png

展开base 也是不少东西,先来看看这个头文件定义了什么吊炸天的东西
一开始看就尼玛就卡主了,

#ifdef __cplusplus
#define YY_EXTERN_C_BEGIN extern "C" {
#define YY_EXTERN_C_END }
#else
#define YY_EXTERN_C_BEGIN
#define YY_EXTERN_C_END
#endif

这是什么鬼啊,各种百度,google之后总算是有些眉目了
先看 #ifdef cplusplus google 之后发现 c++中定义了cplusplus,C语言中没有该定义。即这个是用来:识别是c代码还是c++代码
在看EXTERN_C ,调用extern "C"会让c++编译器按照c的编译格式来编译。多用于c++库的头文件。 也即是说这个在c++的环境下,会强制编译器用 C 语言的规则去编译,而不是用 C++ 语言的规则。。因为 C++ 为了实现函数重载会把函数名和参数等联合起来合成一个中介的函数名,如果 C 函数也被这样编译会出问题,所以在我看来这个是为了兼容c++环境不会编译报错。
好吧解决了这个 在往下看看有什么比较难理解的东西
接下来是一个两重判断的 宏定义

#ifndef YY_CLAMP // return the clamped value
#define YY_CLAMP(_x_, _low_, _high_) (((_x_) > (_high_)) ? (_high_) : (((_x_) < (_low_)) ? (_low_) : (_x_)))
#endif

咋一看不知道有什么用写成if else 语句大概是

    if(x>high)
return high;
else
{
if(x<low)
return low;
else
return x;
}

好吧接下来往下看吧

#ifndef YY_SWAP // swap two value
#define YY_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0)
#endif

虽然这个一眼就看出来是个交换 函数还是有两个地方需要注意的为什么要用这个关键字呢typeof 还有为什么要用do while 直接用if else 不是就行了吗
百度一下你就知道
typeof(var) 是gcc对C语言的一个扩展保留字,用于声明变量类型,var可以是数据类型(int, char*..),也可以是变量表达式。也即是说 变量a 还可以是个变量表达式这里吊吊的
,但为啥要用do while 这里就请众大神指点迷津了。

就不一一解析,看一些比较特别的吧

#ifndef YYSYNTH_DUMMY_CLASS
#define YYSYNTH_DUMMY_CLASS(_name_) \
@interface YYSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
@implementation YYSYNTH_DUMMY_CLASS_ ## _name_ @end
#endif

这一段还是挺特别的, 但oc 编译静态库 要在build setting other linker flag设置 -all_load 和-Objc才可以将category 编进去
使用这段宏定义他可以虚拟新建一个与名字category 相同.h.m 让编译器 编译通过
但是每次要对所有category 都要申明这个的话可能会比较麻烦,直接加-all_load 据官方文档说,它可以使生成的可执行文件较大,并且产生一些不需要的文件

#ifndef YYSYNTH_DYNAMIC_PROPERTY_OBJECT
#define YYSYNTH_DYNAMIC_PROPERTY_OBJECT(_getter_, _setter_, _association_, _type_) \
- (void)_setter_ : (_type_)object { \
[self willChangeValueForKey:@#_getter_]; \
objc_setAssociatedObject(self, _cmd, object, OBJC_ASSOCIATION_ ## _association_); \
[self didChangeValueForKey:@#_getter_]; \
} \
- (_type_)_getter_ { \
return objc_getAssociatedObject(self, @selector(_setter_:)); \
}
#endif
//调用的方法
@interface NSObject (MyAdd)
@property (nonatomic, retain) UIColor *myColor;
@end #import <objc/runtime.h>
@implementation NSObject (MyAdd)
YYSYNTH_DYNAMIC_PROPERTY_OBJECT(myColor, setMyColor, RETAIN, UIColor *)
@end

使用runtime 运行时属性 给catetory 动态添加属性

 
参考链接:
1.http://www.jianshu.com/p/a5acbed59ef3
2.http://blog.csdn.net/komyself/article/details/7778843

#ifdef,#else,#endif,#if 拾忆的更多相关文章

  1. C/C++预处理指令#define,#ifdef,#ifndef,#endif…

    2016年12月29日更新: 今天查看以前文件的时候, 突然发现了#error 这个预处理指令.然后回想一下工作, 发现这个指令使用场景还是很多的.比如: 一个项目的模块儿之多,源文件之大,代码之多, ...

  2. 【转载】C/C++中#ifdef和#endif的用法

    转于 http://www.cnblogs.com/renyuan/archive/2013/05/22/3092362.html 今天笔试的时候遇到这个问题,整理一下! 一般情况下,源程序中所有的行 ...

  3. #ifdef __cplusplus extern "C" { #endif //一段代码 #ifdef __cplusplus } #endif

    这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入" ...

  4. 转:C++中 #ifdef 和#endif的作用

    一般情况下,源程序中所有的行都参加编译.但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是"条件编译".有时,希望当满足某条件时对一组 ...

  5. #ifdef #else #endif #if #ifndef 的用法

    预编译就是在对源文件进行处理之前(如在语法扫描和分析之前),先处理预处理部分,精简代码,然后再进行编译. 预处理命令有:#include 文件包含.#define 宏定义.以及要重点讲的#if.#if ...

  6. C/C++预处理指令#define,#ifdef,#ifndef,#endif… (转)

    本文转自博文C/C++预处理指令#define,#ifdef,#ifndef,#endif….这篇博文写得特别好,特转载. 本文主要记录了C/C++预处理指令,常见的预处理指令如下: #空指令,无任何 ...

  7. .Net拾忆:从List去除重复-拾忆集合

    方法1: private static List<int> DistinctList(List<int> list) {//去除重复 HashSet<int> ha ...

  8. #ifdef #else #endif 的用法

    预处理就是在进行编译的第一遍词法扫描和语法分析之前所作的工作.说白了,就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译.这样做的好处是,经过处理后的代码,将会变的很精短. 关 ...

  9. C语言预处理命令之条件编译(#ifdef,#else,#endif,#if等)

    转自:http://www.kuqin.com/language/20090806/66164.html 预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器.可见预处理过程先于编译器 ...

随机推荐

  1. WPF Image Binding Uri Source 失败解决办法

    在ListView 的ListItem里动态绑定Image. 首先代码写的是没有问题的.但最后运行却无法显示图片.先看代码: 1. XAML部分 代码如下: <ListView x:Name=& ...

  2. css margin相关问题及应用

    一.margin常见问题 1.IE6下双边距问题 margin双布局可以说是IE6下经典的bug之一.产生的条件是:block元素+浮动+margin. 2.maring重叠的问题 css2.0规范对 ...

  3. 绝对路径&相对路径

    被绝对路径和相对路径搞疯了,好多地方不一样,从今天开始,记录下来每次关于这个问题的记录,以备查用 css文件里: 绝对路径:以"/"开头,表示从项目的根目录开始

  4. Qt事件和事件循环

    在处理QT循环事件的时候遇到了问题,查了半天资料都没弄明白问题出在哪,后来找大牛同事问了一下,同事就给我写了QCoreApplication::processEvent()这个函数,好啦,终于搞定了, ...

  5. docker多主机网络方案

    本文探讨Docker多主机网络的性能. 在过去的博文里,我测试过 Docker的网络 . MySQL服务器团队 提供了他们自己的结果,和我的观察是一致的. 本文里一系列的测试,想更多关注使用多主机的D ...

  6. 安装TensorFlow的步骤

    安装步骤: 1.安装虚拟机: 2.安装liunx系统: 3.安装TensorFlow. 1.安装虚拟机:虚拟机的版本是不能太低的.我使用的是:VMware-workstation-full-12.0. ...

  7. thinkphp pdo 重写问题

    ThinkPHP3.2.3版本数据库驱动采用PDO完全重写,配置和使用上面也比之前版本更加灵活和强大,我们来了解下如何使用. 首先,3.2.3的数据库配置信息有所调整,完整的数据库设置包括: 复制代码 ...

  8. Spring.net 学习IOC------属性注入

    我们就完成我们的第一个spring.net学习IOC的"hello world!". 1> 我们新建一个C# 的控制台项目名为Spring,然后引入Spring.Core.d ...

  9. @Autowired注解(转)

    5.6.4 @Autowired注解 自Spring诞生以来,

  10. python zipfile 文件压缩和文件

    文件压缩 zipfile_obj = zipfile.ZipFile(zipfile_objpath, 'a', zipfile.ZIP_DEFLATED) for dirpath, dirnames ...