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 #else#if 中的一個語句
9 #endif 結束前處理器條件
10 #error stderr上列印錯誤訊息
11 #pragma 使用標準化方法向編譯器發出特殊命令

1. 前處理器範例

分析以下範例以了解各種巨集的指令。

#define MAX_ARRAY_LENGTH 20

該指令告訴OCPP20替換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,並將FILE_SIZE定義為42

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

它告訴OCPP僅在尚未定義MESSAGE巨集時才定義MESSAGE

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

它告訴OCPP如果定義了DEBUG,則執行包含語句的過程。 如果在編譯時將- DDEBUG標誌傳遞給gcc編譯器,這將非常有用。 它將定義DEBUG,因此可以在編譯期間動態開啟和關閉偵錯。

2. 預定義的巨集

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 中的上述程式碼時,它會產生以下結果 -

2018-11-15 08:44:54.041 main[50640] File :main.m
2018-11-15 08:44:54.042 main[50640] Date :Nov 15 2018
2018-11-15 08:44:54.042 main[50640] Time :08:44:52
2018-11-15 08:44:54.042 main[50640] Line :7
2018-11-15 08:44:54.043 main[50640] ANSI :1

3. 前處理器運算子

Objective-C前處理器提供以下運算子來建立巨集 -

3.1. 巨集延續(\)

巨集通常必須包含在一行中。巨集延續運算子用於繼續對於單行來說太長的巨集。 例如 -

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

3.2. 字串化(#)

字串化或數位符號運算子(#)在巨集定義中使用時,將巨集引數轉換為字串常數。 此運算子只能在具有指定引數或引數列表的巨集中使用。 例如 -

#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;
}

執行上面範例程式碼,得到以下結果:

2018-11-15 08:56:38.088 main[98681] Carole and Debra: We love you!

3.3. 令牌貼上(##)

巨集定義中的令牌貼上運算子(##)組合了兩個引數。 它允許將巨集定義中的兩個單獨的標記連線到一個標記中。 例如 -

#import <Foundation/Foundation.h>

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

int main(void) {
   int token34 = 40;

   tokenpaster(34);
   return 0;
}

執行上面範例程式碼,得到以下結果:

2018-11-15 08:58:04.872 main[138839] token34 = 40

它是如何發生的,因為這個例子導致前處理器的以下實際輸出 -

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

此範例顯示令牌##ntoken34的串聯,這裡使用了stringizetoken-pasting

3.4. 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;
}

執行上面範例程式碼,得到以下結果:

2018-11-15 09:04:30.790 main[31654] Here is the message: You wish!

4. 引數化巨集

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;
}

執行上面範例程式碼,得到以下結果:

2018-11-15 09:08:15.586 main[64146] Max between 20 and 10 is 20