Objective-C 预处理器


Objective -C 预处理器不是编译器的一部分,而是编译过程中的一个单独步骤。简而言之,Objective-C 预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行所需的预处理。我们将 Objective-C 预处理器称为 OCPP。

所有预处理器命令均以井号 (#) 开头。它必须是第一个非空白字符,并且为了可读性,预处理器指令应从第一列开始。以下部分列出了所有重要的预处理器指令 -

先生。 指令和说明
1

#定义

替换预处理器宏

2

#包括

插入另一个文件中的特定标头

3

#undef

取消定义预处理器宏

4

#ifdef

如果定义了该宏则返回 true

5

#ifndef

如果未定义该宏,则返回 true

6

#如果

测试编译时条件是否为真

7

#别的

#if 的替代方案

8

#elif

#else 和 #if 在一个语句中

9

#万一

有条件地结束预处理器

10

#错误

在 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

__日期__

当前日期为“MMM DD YYYY”格式的字符文字

2

__时间__

当前时间为“HH:MM:SS”格式的字符文字

3

__文件__

这包含当前文件名作为字符串文字。

4

__线__

它包含十进制常量形式的当前行号。

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 的串联,这里我们使用了stringizetoken-pasting

定义的()运算符

预处理器定义的运算符在常量表达式中使用,以确定标识符是否是使用#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;
}

当上面的代码被编译并执行时,它会产生以下结果 -

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