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

  #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. 用NSData和NSFileManager保存内存中的对象

    曾经接触过iOS开发,并且开发过两个应用,纵然青涩,也算是一断美好的回忆.转眼就已经一年多了!现在回过头来决定再次拿起iOS开发. 下面讲NSData: NSdata的概念 1.使用文件时需要频繁地将 ...

  2. android中sqlite3常用命令

    1)打开数据库 在adb shell模式下执行命令sqlite3 + 数据库名称,例如打开email中的EmailProvider.db数据库: 2)sqlite3特殊命令 大多数候,sqlite3读 ...

  3. POJ 1083 Moving Tables

    题意:一个建筑物里有400个房间,房间都在一层里,在一个走廊的两侧,如图,现在要搬n张桌子,告诉你每张桌子是从哪个屋搬到哪个屋,搬桌子的线路之间不可以有重叠,问最少搬几次. 解法:贪心.一开始觉得只要 ...

  4. HDU 4630-No Pain No Game(线段树+离线处理)

    题意: 给你n个数的序列a,q个询问,每个询问给l,r,求在下标i在[l,r]的区间任意两个数的最大公约数中的最大值 分析: 有了hdu3333经验,我们从左向右扫序列,如果当前数的约数在前面出现过, ...

  5. [CODEVS2603]公路修建

    题目描述 某国有n个城市,它们互相之间没有公路相通,因此交通十分不便.为解决这一“行路难”的问题,政府决定修建公路.修建公路的任务由各城市共同完成.    修建工程分若干轮完成.在每一轮中,每个城市选 ...

  6. 内核源码分析之tasklet(基于3.16-rc4)

    tasklet是在HI_SOFTIRQ和TASKLET_SOFTIRQ两个软中断的基础上实现的(它们是在同一个源文件中实现,由此可见它们的关系密切程度),它的数据结构和软中断比较相似,这篇博文将分析t ...

  7. How to interact with the Chef Server using the Chef Server API using Shell script

    !/usr/bin/env bash   _chef_dir () { # Helper function: # Recursive function that searches for chef c ...

  8. Linux下搭建jdk

    1.上oracle下载jdk-8u73-linux-x64.gz文件(其他版本的也可以,不过本教程以.gz格式为准,其他格式没有尝试过,不造怎么弄) 2.在linux桌面上右键->打开终端 3. ...

  9. RabbitMQ三种Exchange模式(fanout,direct,topic)的特性 -摘自网络

    RabbitMQ中,所有生产者提交的消息都由Exchange来接受,然后Exchange按照特定的策略转发到Queue进行存储 RabbitMQ提供了四种Exchange:fanout,direct, ...

  10. H264编码参数的一些小细节

    一次写播放器,基于ijkplayer.在播放一些网络视频的时候,发现无论怎么转码,视频比例始终不对.即便获取了分辨率,但是播放的时候,view不是分辨率比例的那个长宽比.使用ffmpeg查看了一下属性 ...