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

使用标准化方法向编译器发出特殊命令


预处理器实例

分析以下实例以了解各种指令。

  1. #define MAX_ARRAY_LENGTH 20

该指令告诉 OCPP 使用 20 替换 MAX_ARRAY_LENGTH 的实例。为常量使用 #define 以增加可读性。

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

这些指令让 OCPPFoundation Framework 获取 foundation.h,并将文本添加到当前源文件中。下一行告诉 OCPP 获取 myheader.h,并将内容添加到当前源文件。

  1. #undef FILE_SIZE
  2. #define FILE_SIZE 42

OCPP 取消定义存在的 FILE_SIZE 并将其定义为 42。

  1. #ifndef MESSAGE
  2. #define MESSAGE "You wish!"
  3. #endif

OCPP 仅当 MESSAGE 消息尚未定义时才定义 MESSAGE。

  1. #ifdef DEBUG
  2. /* 在这里调试语句 */
  3. #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。

试一试下面的实例:

  1. #import <Foundation/Foundation.h>
  2. int main() {
  3. NSLog(@"File :%s\n", __FILE__ );
  4. NSLog(@"Date :%s\n", __DATE__ );
  5. NSLog(@"Time :%s\n", __TIME__ );
  6. NSLog(@"Line :%d\n", __LINE__ );
  7. NSLog(@"ANSI :%d\n", __STDC__ );
  8. return 0;
  9. }

结果如下:

  1. 2022-07-07 16:46:14.859 demo[20683] File :main.m
  2. 2022-07-07 16:46:14.859 demo[20683] Date :Sep 14 2013
  3. 2022-07-07 16:46:14.859 demo[20683] Time :04:46:14
  4. 2022-07-07 16:46:14.859 demo[20683] Line :8
  5. 2022-07-07 16:46:14.859 demo[20683] ANSI :1

预处理器运算符

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

宏扩展 ()

宏通常必须包含在一行中。宏连续运算符用于连续一行过长的宏。

  1. #define message_for(a, b) \
  2. NSLog(@#a " and " #b ": We love you!\n")
Stringize (#)

stringize 或 数字-符号运算符 ('#')在宏定义中使用时,将宏参数转换为字符串常量。此运算符只能在具有指定参数或参数列表的宏中使用。

  1. #import <Foundation/Foundation.h>
  2. #define message_for(a, b) \
  3. NSLog(@#a " and " #b ": We love you!\n")
  4. int main(void) {
  5. message_for(Carole, Debra);
  6. return 0;
  7. }

结果如下:

  1. 2022-07-07 16:46:14.859 demo[20683] Carole and Debra: We love you!
Token Pasting (##)

宏定义中的令牌粘贴(Token Pasting)运算符(##)组合了两个参数。它允许宏定义中的两个单独的令牌合并为一个令牌。

  1. #import <Foundation/Foundation.h>
  2. #define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)
  3. int main(void) {
  4. int token34 = 40;
  5. tokenpaster(34);
  6. return 0;
  7. }

结果如下:

  1. 2022-07-07 16:48:14.859 demo[20683] token34 = 40

它是如何发生的,因为这个例子导致了预处理器的以下实际输出:

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

这个例子展示了 token##ntoken34 的连接,在这里我们使用了 stringizetoken-pasting 这两种方法。

defined() 运算符

在常量表达式中使用预处理器定义的运算符来确定标识符是否使用 #define 定义。如果定义了指定的标识符,则该值为 true(非零)。如果未定义符号,则该值为 false(零)。定义的运算符指定如下:

  1. #import <Foundation/Foundation.h>
  2. #if !defined (MESSAGE)
  3. #define MESSAGE "You wish!"
  4. #endif
  5. int main(void) {
  6. NSLog(@"Here is the message: %s\n", MESSAGE);
  7. return 0;
  8. }

结果如下:

  1. 2022-07-07 16:48:19.859 demo[20683] Here is the message: You wish!

参数化宏

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

  1. int square(int x) {
  2. return x * x;
  3. }

我们可以使用宏重写上述代码,如下所示:

  1. #define square(x) ((x) * (x))

带有参数的宏必须使用 #define 指令定义,然后才能使用。参数列表用括号括起来,必须紧跟在宏名称后面。宏名称和开括号之间不允许有空格。例如:

  1. #import <Foundation/Foundation.h>
  2. #define MAX(x,y) ((x) > (y) ? (x) : (y))
  3. int main(void) {
  4. NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));
  5. return 0;
  6. }

结果如下:

  1. 2022-07-07 16:52:15.859 demo[20683] Max between 20 and 10 is 20