Objective-C 预处理器 - Objective-C教程

Objective-C的预处理器是不是编译器的一部分,但是在编译过程中是一个单独的步骤。在简单的条件,是一个Objective-C预处理器只是一个文本替换工具,它指示编译器做实际的编译之前需要预先处理。我们会参考Objective-C的预处理为OCPP。

井号(#)开头的所有预处理命令。它必须是第一个非空字符,可读性,预处理器指令应在第一列开始。下节列出了所有重要的预处理指令:

指示 描述
#define 替代预处理宏
#include 从另一个文件中插入一个特定的头
#undef 取消定义预处理宏
#ifdef 如果定义了这个宏返回true
#ifndef 返回true,如果该宏没有被定义
#if 编译时间条件下的测试,如果是true
#else #if 替代方案
#elif #else 和 #if 在一个语句
#endif 结束预处理条件
#error stderr上打印错误消息
#pragma 编译器使用一个标准化的方法发出特殊命令

预处理器实例

分析下面的例子来了解各种指令。

#define MAX_ARRAY_LENGTH 20

该指令告诉OCPP,以取代实例MAX_ARRAY_LENGTH为20。使用#定义常数,以增加可读性。

#import <Foundation/Foundation.h>
#include "myheader.h"

这些的指令告诉OCPP得到 foundation.h 基础框架和文本添加到当前源文件。下一行告诉 OCPP 从本地目录得到 myheader.h 的内容添加到当前源文件。

#undef  FILE_SIZE
#define FILE_SIZE 42

告诉OCPP 取消对现有 FILE_SIZE 的定义,并把它定义为42 。

#ifndef MESSAGE
   #define MESSAGE "You wish!"
#endif

这告诉OCPP定义消息,如果消息没有被定义。

#ifdef DEBUG
   /* Your debugging statements here */
#endif

这告诉OCPP如果DEBUG被定义语句随附过程。如果传递 DDEBUG标志,gcc编译器在编译时,这是非常有用的。这将定义DEBUG,所以可以在编译过程中打开调试和关闭。

预定义宏

ANSI C定义了一些宏。虽然每一个都是供编程使用,预定义宏不应该被直接修改。

Macro 描述
DATE The current date as a character literal in "MMM DD YYYY" format
TIME The current time as a character literal in "HH:MM:SS" format
FILE This contains the current filename as a string literal.
LINE This contains the current line number as a decimal constant.
STDC Defined as 1 when the compiler complies with the ANSI standard.

让我们试试下面的例子:

#import <Foundation/Foundation.h>

int main()
{
   NSLog(@"File :%s
", __FILE__ );
   NSLog(@"Date :%s
", __DATE__ );
   NSLog(@"Time :%s
", __TIME__ );
   NSLog(@"Line :%d
", __LINE__ );
   NSLog(@"ANSI :%d
", __STDC__ );

   return 0;
}

文件 main.m 上面的代码在编译和执行时,它会产生以下结果:

2013-09-14 04:46:14.859 demo[20683] File :main.m
2013-09-14 04:46:14.859 demo[20683] Date :Sep 14 2013
2013-09-14 04:46:14.859 demo[20683] Time :04:46:14
2013-09-14 04:46:14.859 demo[20683] Line :8
2013-09-14 04:46:14.859 demo[20683] ANSI :1

预处理运算符

Objective-C 预处理器提供了运算符,以帮助创建宏:

宏延续()

宏通常必须包含在一个单一的行。宏延续运算符用于继续宏的一行。例如:

#define  message_for(a, b)  
    NSLog(@#a " and " #b ": We love you!
")
Stringize (#)

stringize或数字符号运算符('#'),在宏定义内使用时,一个宏参数转换成一个字符串常量。此操作只可用于在宏具有指定参数或参数列表。例如:

#import <Foundation/Foundation.h>

#define  message_for(a, b)  
    NSLog(@#a " and " #b ": We love you!
")

int main(void)
{
   message_for(Carole, Debra);
   return 0;
}

让我们编译和运行上面的程序,这将产生以下结果:

2013-09-14 05:46:14.859 demo[20683] Carole and Debra: We love you!
标记粘贴 (##)

标记粘贴运算符(##)在宏定义内结合两个参数。在宏定义,允许两个单独的令牌必须合并成一个单一的令牌。例如:

#import <Foundation/Foundation.h>

#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)

int main(void)
{
   int token34 = 40;

   tokenpaster(34);
   return 0;
}

让我们编译和运行上面的程序,这将产生以下结果:

2013-09-14 05:48:14.859 demo[20683] token34 = 40

它是如何发生的,因为这个例子中的结果在下面从预处理的实际输出:

NSLog (@"token34 = %d", token34);

这个例子显示了串联令牌#n为进token34,这里我们使用stringize和标记粘贴。

defined() 运算符

预处理器定义的常量表达式运算符用于确定是否使用#define定义一个标识符。如果指定的标识符被定义,该值是真(非零)。如果符号没有定义,这个值是false(零)。定义的运算符规定如下:

#import <Foundation/Foundation.h>

#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

int main(void)
{
   NSLog(@"Here is the message: %s
", MESSAGE);  
   return 0;
}

让我们编译和运行上面的程序,这将产生以下结果:

2013-09-14 05:48:19.859 demo[20683] Here is the message: You wish!

参数化宏

OCPP的一个强大功能是能够模拟函数,使用参数化宏。例如,我们可能有一些代码方数字如下:

int square(int x) {
   return x * x;
}

我们可以重写上面的代码中使用宏如下:

#define square(x) ((x) * (x))

带参数的宏,必须使用才可以使用#define指令定义。参数列表括在括号中,必须紧跟在宏名。宏名和左括号之间不允许有空格。例如:

#import <Foundation/Foundation.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void)
{
   NSLog(@"Max between 20 and 10 is %d
", MAX(10, 20));  
   return 0;
}

让我们编译和运行上面的程序,这将产生以下结果:

2013-09-14 05:52:15.859 demo[20683] Max between 20 and 10 is 20