程序中的源代碼計算機是無法識別的,需要將寫好的代碼轉成0、1二進制代碼,計算機才能識別。將源代碼轉成二進制代碼的需要經過兩步,編譯和鏈接。編譯是通過編譯器將每個文件的代碼都轉為二進制代碼,在這個過程中,如果有語法錯誤,會有編譯失敗的提示,如果成功,那么會生成對應多個目標文件。在一個文件中可能會用到其他文件,因此,還需要將編譯生成的目標文件和系統提供的文件組合到一起,這個過程就是鏈接。經過鏈接,最后生成了可執行文件。
通常人們所理解的程序運行就是編譯和鏈接兩個階段,但實際上在編譯之前預處理器要進行預處理操作,處理完之后才進入到編譯階段。因為預處理指令是在編譯之前就行進了,所以它比程序運行時進行操作的效率高。
預處理程序實際上是在分析程序前先處理的語句,它可以識別散步在程序中的特定語句。所有的預處理語句都使用井號(#)開頭,這個符號必須是一行中的第一個非空字符。
預處理語句可以大概劃分成三類:文件包含、宏定義和條件編譯,下面,就一一來講解。
文件包含指的是在當前文件中用到其他文件中的函數或方法或者是其他信息時,可以將其他文件的頭文件包含進來,然后再當前文件中使用,文件包含一般放到文件的開頭位置。
如果使用C語言編程,文件包含是#include<> 或者 #include“”。如果使用Objective-C語言,文件包含為#import<>或者#import“”。#include與#import最大的區別是#import在導入文件的時候進行了去重復檢查。此外,“”和<>兩個也是有區別的,“”是用來放自己寫的文件,<>用來放系統文件。程序在執行的時候,會根據你寫的樣式,優先去尋找對應類型的文件。比如<>,會先去找系統的文件,如果找不到,再去找自定義的文件。所以,正確的選擇樣式,能夠提高程序的運行效率。
在使用文件包含的時候,會遇到A文件中用到B文件,B文件中用到A文件,這種相互使用包含的關系,這種情況就有點像死循環了,要使用A文件,必須要先有B,可是在B中,又需要先有A,因此在運行的時候,會出現錯誤。解決這個問題最好的辦法,是用@class代替文件包含,@class就是表明有這個類,等在源文件中真正用到的時候再包含文件。
代碼:
// A.h// Test//// Created by jerei on 15-7-24.// Copyright (c) 2015年 jerehedu. All rights reserved.//#import <Foundation/Foundation.h>@class B;@interface A : NSObject@PRoperty (nonatomic, strong) B *obj;@end//// B.h// Test//// Created by jerei on 15-7-24.// Copyright (c) 2015年 jerehedu. All rights reserved.//#import <Foundation/Foundation.h>@class A;@interface B : NSObject@property (nonatomic, strong) A *obj;@end
在程序中,有一些常量或者簡短的函數是會多次重復使用的,對于這些常用的數據,我們可以使用宏定義。使用宏定義可以快速的完成程序中多處的配置,最大的好處是只要修改宏定義的值,所有使用宏定義的值都會發生改變。此外,宏定義是在程序編譯之前進行替換和設置,比定義成全局變量或函數的效率要高。
宏定義是通過#define來實現的,一般寫在程序的文件包含的下面。宏名通常用全部的大寫字母表示。下面,就通過代碼舉來看一下宏定義的使用。
代碼:
// main.m// Test//// Created by jerei on 15-7-24.// Copyright (c) 2015年 jerehedu. All rights reserved.//#import <Foundation/Foundation.h>#define JR_PI 3.14#define JR_MAX(a,b) ((a>b)?(a):(b)) //得到兩個數中較大值#define JR_SQUARE_1(n) n*n //求數字的平方#define JR_SQUARE_2(n) (n)*(n) //求數字的平方#define JR_HELLO @"hello world";int main(int argc, const char * argv[]) { @autoreleasepool { int num1 = JR_MAX(1, 2); NSLog(@"max = %i",num1); //結果: max = 2 int num2 = JR_SQUARE_1(2); NSLog(@"2的平方 = %i",num2); //結果:2的平方 = 4 int num3 = JR_SQUARE_1(2+1); NSLog(@"(2+1)的平方 = %i",num3); //結果:(2+1)的平方 = 5 int num4 = JR_SQUARE_2(2+1); NSLog(@"(2+1)的平方 = %i",num4); //結果:(2+1)的平方 = 9 } return 0;}
在代碼中可以看到,同樣都是求一個數的平方,但是兩個宏定義得到的結果卻是不一樣的。第一個計算2+1的平方的時候是2+1*2+1,所以結果為5,答案錯誤。因此,在寫宏定義,帶參數的時候,需要設置小括號,保證正確性。
條件編譯其實就在編譯之前由預處理器來根據預處理語句進行判斷,如果滿足條件,就編譯滿足條件下面的代碼段,如果不滿足條件,下面的代碼段就不進入編譯環節。
條件編譯主要分為兩種,一種是判斷是否定義過某個宏,根據是否定義過這個宏,來決定是否編譯某段代碼。另外,還有一組語句和條件結構中的階梯if結構非常類似,但是寫法上有所區別,是#if、#elif、#else、#endif組成。需要注意的是,無論哪種,都要有#endif結束標志。此外,最重要的一點是,條件編譯中的條件不能使用普通的變量,一般會選擇使用宏定義。
代碼:
// main.m// Test//// Created by jerei on 15-7-24.// Copyright (c) 2015年 jerehedu. All rights reserved.//#import <Foundation/Foundation.h>#define JR_COUNT 10int main(int argc, const char * argv[]) { @autoreleasepool { #if defined(JR_COUNT) NSLog(@"定義了 COUNT 這個宏");#endif #if defined(JR_MAX) NSLog(@"沒有定義了 JR_MAX 這個宏");#endif #if JR_COUNT==1 NSLog(@"JR_COUNT=1");#elif JR_COUNT==2 NSLog(@"JR_COUNT=2");#elif JR_COUNT==3 NSLog(@"JR_COUNT=3");#else NSLog(@"JR_COUNT=%i",JR_COUNT);#endif } return 0;}
疑問咨詢或技術交流,請加入官方QQ群: (452379712)
|
新聞熱點
疑難解答