iOS开发笔记系列-基础6(预处理程序)
预处理程序提供了一些工具,使用这些工具更易于开发、阅读、修改程序,也易于将程序移植到不同的系统中。又称为宏。
#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(预处理程序)的更多相关文章
- iOS开发笔记系列-基础1(数据类型与表达式)
学习iOS开发快两年了,去年完成MagViewer之后就因为公司的其他业务繁重,除了维护这个应用之外,只是断断续续地自己做一些实验开发,没有再发布新的应用,这里想整理一下学习过程中的笔记,以便加深印象 ...
- iOS开发笔记系列-基础2(类)
面向对象编程总是离不开类和对象的,Objective-C也不例外,不过Objective-C中的类还有一些自己的独特点. 类的声明和定义 在iOS开发中,类的声明与定义通常都是分开的,类得声明通常存放 ...
- iOS开发笔记系列-基础5(分类和协议)
分类 在Objective-C中,除了通过新建子类的方式来向类添加新方法外,还可以通过分类的方式.分类提供了一种简单的方式,将类的定义模块化到相关方法的组或分类中,它还提供了扩展现有类定义的简便方式, ...
- iOS开发笔记系列-基础3(多态、动态类型和动态绑定)
多态:相同的名称,不同的类 使不同的类共享相同方法名称的能力成为多态.它让你可以开发一组类,这组类中的每一个类都能响应相同的方法名.每个类的定义都封装了响应特定方法所需要的代码,这使得它独立于其他的类 ...
- iOS开发笔记系列-基础7(C语言特性)
Objective-C是C语言的扩展,因此,也具备很多C语言的基本特性,这里只罗列部分. 块(Blocks) 块是对C语言的一种扩展,它并未作为标准ANSI C所定义的部分,而是Apple添加到语言中 ...
- iOS开发笔记系列-基础4(变量与数据类型)
对象的初始化 对象的初始化方法一般都如下: -(id)init { self=[super init]; if(self){ ... } return self; } 这个方法首先会调用父类的初始化方 ...
- IOS科研IOS开发笔记学习基础知识
这篇文章是我的IOS学习笔记,他们是知识的基础,在这里,根据记录的查询后的条款. 1,UIScrollView能完毕滚动的功能. 示比例如以下: UIScrollView *tableScrollVi ...
- iOS开发技巧系列---详解KVC(我告诉你KVC的一切)
KVC(Key-value coding)键值编码,单看这个名字可能不太好理解.其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值.而不需 ...
- 【Swift】iOS开发笔记(二)
前言 这个系列主要是一些开发中遇到的坑记录分享,有助于初学者跨过这些坑,攒够 7 条发一篇. 声明 欢迎转载,但请保留文章原始出处:) 博客园:http://www.cnblogs.com 农民伯 ...
随机推荐
- vc/mfc获取rgb图像数据后动态显示及保存图片的方法
vc/mfc获取rgb图像数据后动态显示及保存图片的方法 该情况可用于视频通信中获取的位图数据回放显示或显示摄像头捕获的本地图像 第一种方法 #include<vfw.h> 加载 vfw3 ...
- 调试WEB APP多设备浏览器(转)
方法:adobe shadow \ opera远程调试\ weinre adobe shadow: 我们经常使用Firefox的firebug或者Chrome的开发人员工具进行Web调试页面,J ...
- java classpath、path用法
java环境配置classpath和path变量的作用: path:指定cmd中命令执行文件所在的路径.比如javac.java两个可执行文件在jdk的bin目录下,如果path值含有这个bin目录, ...
- ASP.NET缓存策略经验谈
要提升ASP.NET应用程序的性能,最简单.最有效的方式就是使用内建的缓存引擎.虽然也能构建自己的缓存,但由于缓存引擎已提供了如此多的功能,所以完全不必如此麻烦.在很大程度上,ASP.NET开发者在W ...
- C# 一次查询多表,填充DataSet并指定表名
lhrhi 原文 NET 一次查询多表,填充DataSet并指定表名(DataSet指定DataTable名称的技巧) 现实中的场景,有时可能需要一次查询数据库中表张.在使用SqlDataAdapte ...
- HDU-4882 ZCC Loves Codefires
http://acm.hdu.edu.cn/showproblem.php?pid=4882 ZCC Loves Codefires Time Limit: 2000/1000 MS (Java/Ot ...
- Linux基本命令(5)管理使用者和设立权限的命令
管理使用者和设立权限的命令 命令 说明 命令 说明 chmod 用来改变权限 useradd 用来增加用户 su 用来修改用户 5.1 chmod命令 chmod命令用来改变许可权限.读取.写 ...
- 线性模型(3):Logistic Regression
此笔记源于台湾大学林轩田老师<机器学习基石><机器学习技法> (一)Logistic Regression 原理 对于分类问题,假设我们想得到的结果不是(x属于某一类)这种形式 ...
- bzoj 1391 [Ceoi2008]order(最小割)
[题意] 有n个有偿工作选做,m个机器,完成一个工作需要若干个工序,完成每个工序需要一个机器,对于一个机器,在不同的工序有不同的租费,但买下来的费用只有一个.问最大获益. [思路] 对于工作和机器建点 ...
- [算法] 希尔排序 Shell Sort
希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进.该方法又称缩小增量排序,因DL.Shell于1959年提出而得名. 希尔排序实质上是一种分组插入方法.它的基本思想是: ...