Objective-C 预处理器
Objective-C 预处理器 不是编译器的一部分,而是编译过程中的一个独立步骤。 简单来说,Objective-C 预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行必要的预处理。 我们将 Objective-C 预处理器称为 OCPP。
所有预处理器命令都以井号 (#) 开头。 它必须是第一个非空字符,并且为了可读性,预处理器指令应该从第一列开始。 以下部分列出了所有重要的预处理器指令 −
序号 | 指令 & 描述 |
---|---|
1 | #define 替换预处理器宏 |
2 | #include 从另一个文件插入一个特定的头 |
3 | #undef 取消定义预处理器宏 |
4 | #ifdef 如果定义了这个宏则返回真 |
5 | #ifndef 如果这个宏没有定义则返回真 |
6 | #if 测试编译时条件是否为真 |
7 | #else #if 的替代方法 |
8 | #elif #else 和 #if 在一条语句中 |
9 | #endif 有条件地结束预处理器 |
10 | #error 在 stderr 上打印错误信息 |
11 | #pragma 使用标准化方法向编译器发出特殊命令 |
预处理器示例
分析以下示例以了解各种指令。
#define MAX_ARRAY_LENGTH 20
此指令告诉 OCPP 将 MAX_ARRAY_LENGTH 的实例替换为 20。使用 #define 作为常量以提高可读性。
#import <Foundation/Foundation.h> #include "myheader.h"
这些指令告诉 OCPP 从 Foundation Framework 获取 foundation.h 并将文本添加到当前源文件。 下一行告诉 OCPP 从本地目录获取 myheader.h 并将内容添加到当前源文件中。
#undef FILE_SIZE #define FILE_SIZE 42
这告诉 OCPP 取消定义现有的 FILE_SIZE 并将其定义为 42。
#ifndef MESSAGE #define MESSAGE "You wish!" #endif
这告诉 OCPP 仅当 MESSAGE 尚未定义时才定义 MESSAGE。
#ifdef DEBUG /* Your debugging statements here */ #endif
如果定义了 DEBUG,这将告诉 OCPP 执行包含的语句的过程。 如果您在编译时将 -DDEBUG 标志传递给 gcc 编译器,这将很有用。 这将定义 DEBUG,因此您可以在编译期间即时打开和关闭调试。
预定义宏
ANSI C 定义了许多宏。 尽管每个宏都可供您在编程中使用,但不应直接修改预定义的宏。
序号 | 宏 & 描述 |
---|---|
1 | __DATE__ 当前日期作为"MMM DD YYYY"格式的字符文字 |
2 | __TIME__ "HH:MM:SS"格式的字符文字形式的当前时间 |
3 | __FILE__ 这包含当前文件名作为字符串文字。 |
4 | __LINE__ 这包含作为十进制常量的当前行号。 |
5 | __STDC__ 当编译器符合ANSI标准时定义为1。 |
让我们试试下面的例子 −
#import <Foundation/Foundation.h> int main() { NSLog(@"File :%s\n", __FILE__ ); NSLog(@"Date :%s\n", __DATE__ ); NSLog(@"Time :%s\n", __TIME__ ); NSLog(@"Line :%d\n", __LINE__ ); NSLog(@"ANSI :%d\n", __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!\n")
字符串化(#)
字符串化或数字符号运算符 ('#') 在宏定义中使用时,将宏参数转换为字符串常量。 此运算符只能在具有指定参数或参数列表的宏中使用。 例如 −
#import <Foundation/Foundation.h> #define message_for(a, b) \ NSLog(@#a " and " #b ": We love you!\n") 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);
这个例子展示了 token##n 到 token34 的串联,这里我们使用了 stringize 和 token-pasting。
defined() 运算符
预处理器 defined 运算符用于常量表达式,以确定标识符是否使用 #define 定义。 如果定义了指定的标识符,则该值为真(非零)。 如果未定义该符号,则该值为假(零)。 定义的运算符指定如下 −
#import <Foundation/Foundation.h> #if !defined (MESSAGE) #define MESSAGE "You wish!" #endif int main(void) { NSLog(@"Here is the message: %s\n", 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\n", MAX(10, 20)); return 0; }
当上面的代码被编译和执行时,会产生如下结果 −
2013-09-14 05:52:15.859 demo[20683] Max between 20 and 10 is 20