Objective-C 预处理器
Objective-C 预处理器 不是编译器的一部分,而是编译过程中的一个单独步骤。简单来说,Objective-C 预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行所需的预处理。我们将 Objective-C 预处理器称为 OCPP。
所有预处理器命令都以磅符号(#
)开头。它必须是第一个非空字符,为了可读性,预处理器指令应该从第一列开始。下一节列出了所有重要的预处理器指令:
编号 | 指令 & 描述 |
---|---|
1 | #define 替换预处理器宏 |
2 | #include 插入另一个文件的特定头 |
3 | #undef 取消定义预处理器宏 |
4 | #ifdef 如果定义了此宏,则返回 true |
5 | #ifndef 如果未定义此宏,则返回 true |
6 | #if 测试编译时条件是否为 true |
7 | #else #if 的另一种结果 |
8 | #elif 一个判断语句中的另外一个 #if |
9 | #endif 预处理器的结束 |
10 | #error 在 stderr 上打印错误消息 |
11 | #pragma 使用标准化方法向编译器发出特殊命令 |
预处理器实例
分析以下实例以了解各种指令。
#define MAX_ARRAY_LENGTH 20
该指令告诉 OCPP 使用 20 替换 MAX_ARRAY_LENGTH
的实例。为常量使用 #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
/* 在这里调试语句 */
#endif
如果定义了 DEBUG,这会告诉 OCPP 处理包含的语句。如果在编译时将 -DDEBUG 标志传递给 gcc 编译器,将非常有用。这将定义调试,因此您可以在编译期间动态打开和关闭调试。
预定义宏
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;
}
结果如下:
2022-07-07 16:46:14.859 demo[20683] File :main.m
2022-07-07 16:46:14.859 demo[20683] Date :Sep 14 2013
2022-07-07 16:46:14.859 demo[20683] Time :04:46:14
2022-07-07 16:46:14.859 demo[20683] Line :8
2022-07-07 16:46:14.859 demo[20683] ANSI :1
预处理器运算符
Objective-C 预处理器提供以下运算符帮助您创建宏:
宏扩展 ()
宏通常必须包含在一行中。宏连续运算符用于连续一行过长的宏。
#define message_for(a, b) \
NSLog(@#a " and " #b ": We love you!\n")
Stringize (#)
stringize 或 数字-符号运算符 ('#')在宏定义中使用时,将宏参数转换为字符串常量。此运算符只能在具有指定参数或参数列表的宏中使用。
#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;
}
结果如下:
2022-07-07 16:46:14.859 demo[20683] Carole and Debra: We love you!
Token Pasting (##)
宏定义中的令牌粘贴(Token Pasting)运算符(##)组合了两个参数。它允许宏定义中的两个单独的令牌合并为一个令牌。
#import <Foundation/Foundation.h>
#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
结果如下:
2022-07-07 16:48:14.859 demo[20683] token34 = 40
它是如何发生的,因为这个例子导致了预处理器的以下实际输出:
NSLog (@"token34 = %d", token34);
这个例子展示了 token##n 与 token34 的连接,在这里我们使用了 stringize 和 token-pasting 这两种方法。
defined() 运算符
在常量表达式中使用预处理器定义的运算符来确定标识符是否使用 #define
定义。如果定义了指定的标识符,则该值为 true(非零)。如果未定义符号,则该值为 false(零)。定义的运算符指定如下:
#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;
}
结果如下:
2022-07-07 16: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;
}
结果如下:
2022-07-07 16:52:15.859 demo[20683] Max between 20 and 10 is 20