预处理程序提供了一些工具,使用这些工具更易于开发、阅读、修改程序,也易于将程序移植到不同的系统中。又称为宏。

  #define

  #define语句的基本用途之一就是给富豪名称指定程序常量。比如:

#define TRUE 1 //没有分号结尾

  此处定义了名称TRUE,并使它等于值1。之后,名称TRUE可用于程序中任何需要常量1的地方,只要出现这个名称,预处理程序自动将这个名称替换为预定义的值1。

  预定义名称不是变量,因此,不能给它赋值,除非替换指定值的结果实际上是个变量。#define语句中预定义名称右边的所有字符都会被预处理程序自动替换到程序中,这类似于文本搜索和替换。

  #define通常放在程序的开始,#import或#include之后,这并不是必须的,它可以出现在程序的任何地方。预定义名称没有局部定义之类得说法,一旦定义一个名称,就可以在程序的任何地方使用它。

  预定义程序假设定义包含在程序的一行中,如果需要第二行,那么上一行的最后一个字符必须是反斜线符号(\)。

  预定义不仅适用于单个的值,也可以是更高级的表达式,比如带有参数的名称即函数宏,在函数宏时,函数名称和参数列表的左括号之间不允许有空格,如下:

(下边的例子摘自网络博文,原文非常优秀)

#define MIN(A,B) A < B ? A : B

int a = MIN(,);
// => int a = 1 < 2 ? 1 : 2;
printf("%d",a);
// => 1

  不过这样很容易出问题,比如:

int a =  * MIN(, );
printf("%d",a);
// => 4

  这是因为预定义只是简单的文本替换,所以表达式变成了 int a=2*3<4?3:4;这里忽略了运算的优先级顺序,因此出错了。改进为

#define MIN(A,B) (A < B ? A : B)

int a = MIN(,  <  ?  : );
printf("%d",a);
// => 4

  但是仍然存在风险,比如:

int a = MIN(, MIN(, ))
//int a = MIN(3, 4 < 5 ? 4 : 5);
// => int a = (3 < 4 < 5 ? 4 : 5 ? 3 : 4 < 5 ? 4 : 5); //希望你还记得运算符优先级
// => int a = ((3 < (4 < 5 ? 4 : 5) ? 3 : 4) < 5 ? 4 : 5); //给这个式子加上了括号
// => int a = ((3 < 4 ? 3 : 4) < 5 ? 4 : 5)
// => int a = (3 < 5 ? 4 : 5)
// => int a = 4

  改进为#define MIN(A,B) ((A) < (B) ? (A) : (B)),这里依然是存在风险的,比如:

float a = 1.0f;
float b = MIN(a++, 1.5f);
printf("a=%f, b=%f",a,b);
// => a=3.000000, b=2.000000

  这是因为展开式为:float b = ((a++) < (1.5f) ? (a++) : (1.5f))。解决这个问题并不是一件很简单的事情,使用的方式也很巧妙。我们需要用到一个GNU C的赋值扩展,即使用({...})的形式。这种形式的语句可以类似很多脚本语言,在顺次执行之后,会将最后一次的表达式的赋值作为返回。举个简单的例子,下面的代码执行完毕后a的值为3,而且b和c只存在于大括号限定的代码域中,如:

int a = ({
int b = ;
int c = ;
b + c;
});
// => a is 3

  因此,宏最终改进为:

#define MIN(A,B)    ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })

这里定义了三个语句,分别以输入的类型申明了__a和__b,并使用输入为其赋值,接下来做一个简单的条件比较,得到__a和__b中的较小值,并使用赋值扩展将结果作为返回。这样的实现保证了不改变原来的逻辑,先进行一次赋值,也避免了括号优先级的问题,可以说是一个比较好的解决方案了。可见,编写复杂的宏需要考虑很多细节。

  

  #import

  #import是引入文件用的,双引号表示引入本地文件,尖括号引入系统文件。#import是#include的升级版,它能够确保该文件只被引入一次,避免重复引用。

  条件编译

  条件编译通常用于创建可以在不同计算机系统上编译运行的程序,它也经常用来开关程序中的各种语句,例如,用来输出变量值的调试语句。

  #ifdef  #endif  #else  #elif  #ifndef  #undef

  比如:

#ifdef IPAD
#define imageF @"ihd.png"
#else
#define imageF @"i.png"
#endif

  只要之前定义过IPAD了,就可以了,并不需要它一定有值,比如 #define IPAD就可以了。

  也可以通过命令行在程序编译时为预编译器定义名称,比如:

  gcc -framework Foundation -D IPAD program.m  就为预处理程序定义了 IPAD,它使program.m中所有#ifdef IPAD都判断为TRUE。这种技术使得不必编辑源程序就可以定义名称了。

  在Xcode的Build Settings中,可以在Preprocessor Macros选项下添加新的预定义名称并制定它们的值,比如常用的有DEBUG来标识是否是调试版本还是正式版本。

iOS开发笔记系列-基础6(预处理程序)的更多相关文章

  1. iOS开发笔记系列-基础1(数据类型与表达式)

    学习iOS开发快两年了,去年完成MagViewer之后就因为公司的其他业务繁重,除了维护这个应用之外,只是断断续续地自己做一些实验开发,没有再发布新的应用,这里想整理一下学习过程中的笔记,以便加深印象 ...

  2. iOS开发笔记系列-基础2(类)

    面向对象编程总是离不开类和对象的,Objective-C也不例外,不过Objective-C中的类还有一些自己的独特点. 类的声明和定义 在iOS开发中,类的声明与定义通常都是分开的,类得声明通常存放 ...

  3. iOS开发笔记系列-基础5(分类和协议)

    分类 在Objective-C中,除了通过新建子类的方式来向类添加新方法外,还可以通过分类的方式.分类提供了一种简单的方式,将类的定义模块化到相关方法的组或分类中,它还提供了扩展现有类定义的简便方式, ...

  4. iOS开发笔记系列-基础3(多态、动态类型和动态绑定)

    多态:相同的名称,不同的类 使不同的类共享相同方法名称的能力成为多态.它让你可以开发一组类,这组类中的每一个类都能响应相同的方法名.每个类的定义都封装了响应特定方法所需要的代码,这使得它独立于其他的类 ...

  5. iOS开发笔记系列-基础7(C语言特性)

    Objective-C是C语言的扩展,因此,也具备很多C语言的基本特性,这里只罗列部分. 块(Blocks) 块是对C语言的一种扩展,它并未作为标准ANSI C所定义的部分,而是Apple添加到语言中 ...

  6. iOS开发笔记系列-基础4(变量与数据类型)

    对象的初始化 对象的初始化方法一般都如下: -(id)init { self=[super init]; if(self){ ... } return self; } 这个方法首先会调用父类的初始化方 ...

  7. IOS科研IOS开发笔记学习基础知识

    这篇文章是我的IOS学习笔记,他们是知识的基础,在这里,根据记录的查询后的条款. 1,UIScrollView能完毕滚动的功能. 示比例如以下: UIScrollView *tableScrollVi ...

  8. iOS开发技巧系列---详解KVC(我告诉你KVC的一切)

    KVC(Key-value coding)键值编码,单看这个名字可能不太好理解.其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值.而不需 ...

  9. 【Swift】iOS开发笔记(二)

    前言 这个系列主要是一些开发中遇到的坑记录分享,有助于初学者跨过这些坑,攒够 7 条发一篇. 声明  欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯 ...

随机推荐

  1. BUFFER CACHE之调整buffer cache的大小

    Buffer Cache存放真正数据的缓冲区,shared Pool里面存放的是sql指令(LC中一次编译,多次运行,加快处理性能,cache hit ratio要高),而buffer cache里面 ...

  2. Java 单元测试(Junit)

    在有些时候,我们需要对我们自己编写的代码进行单元测试(好处是,减少后期维护的精力和费用),这是一些最基本的模块测试.当然,在进行单元测试的同时也必然得清楚我们测试的代码的内部逻辑实现,这样在测试的时候 ...

  3. POJ 1401 Factorial

    题意:求一个数的阶乘最后边有几个0. 解法:如果有0说明这个数含有2和5这两个因子,对于一个阶乘来说因子2的数量一定比5的数量多,所以只要算有几个5就可以了,依次算5的个数,25的个数,125的个数… ...

  4. 2014年acm亚洲区域赛·鞍山站

    今天北京赛站的比赛也结束了···看了一天的直播之后意识到鞍山站的比赛都过去了一个多月了···这一个月比较萎靡···整天都在睡觉写报告画工图中度过··· 鞍山比哈尔滨还是暖和很多的···就是山上有奇怪的 ...

  5. HDU 5875 Function

    Function Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total ...

  6. C# 类与接口的几个问题的总结(待续)

    1. 有关类的多态性 C#中,类的多态性是通过在子类中重载基类的虚方法(virtual标识)或函数成员来实现. 在C#中,继承.虚方法和重写方法组合在一起才能实现多态性. 2. 显式接口成员的使用 显 ...

  7. SQL注入中利用XP_cmdshell提权的用法(转)

    先来介绍一下子服务器的基本情况,windows 2000 adv server 中文版,据称 打过了sp3,asp+iis+mssql .首先扫描了一下子端口,呵呵,开始的一般步骤. 端口21开放: ...

  8. 【SQL server】安装和配置

    (1)SQL sever 版本问题1: SQL sever 2000 .SQL sever 2005.SQL sever 2008 .SQL sever 2008 R2 安装的时候需要注意是SQL s ...

  9. 从python的yield说起

    前段时间在读trac 中wiki模块的源码的时候,发现了很多地方都使用了yiled这一关键词, 感觉是在需要返回某个值的地方通过yield来代替return, 不是很明白其用法,所以仔细研究下. 一个 ...

  10. 【跟我一起学python吧】python chr()、unichr()和ord()

    chr().unichr()和ord() chr()函数用一个范围在range(256)内的(就是0-255)整数作参数,返回一个对应的字符.unichr()跟它一样,只不过返回的是Unicode字符 ...