02-預(yù)處理指令(宏定義 條件編譯 文件包含)撩独、typedef、const常量

目錄

1 預(yù)處理指令
2 typedef
3 typedef和宏定義的區(qū)別
4 const關(guān)鍵字
5 宏定義(define)與常量(const)的選擇

1 預(yù)處理指令


預(yù)處理指令: 在文件翻譯成0和1之前做的操作稱之為預(yù)處理指令, 一般情況預(yù)處理指令都是以#號開頭的

3個預(yù)處理指令: 宏定義 條件編譯 文件包含

1.1 宏定義(只做替換胜卤,不做任何運算)

  • 宏定義的格式 1.不帶參數(shù)的宏定義, 2.帶參數(shù)的宏定義

  • 宏定義的作用: 會在程序翻譯成0和1之前, 將所有宏名替換為宏的值

  • 宏定義在什么時候替換: 源代碼 --> 預(yù)處理 -->匯編 -->二進(jìn)制 -->可執(zhí)行程序

  • 規(guī)范: 一般情況宏名都大寫, 多個單詞之間用_隔開, 并且每個單詞全部大寫. (有的要求宏名以k開頭, 多個單詞之間用駝峰命名)

  • 注意: 1. 宏定義后面不要寫分號, 2. 如果宏名寫在雙引號中, 那么不會被替換

  • 宏定義的作用域: 從定義的那一行開始, 一直到文件末尾, 雖然默認(rèn)情況下宏定義的作用域是從定義的那一行開始, 一直到文件末尾. 但是我們也可以通過對應(yīng)的關(guān)鍵字提前結(jié)束宏定義的作用域

  • 宏定義的使用場景: 獲取屏幕的寬度, 獲取手機(jī)系統(tǒng)版本號, 做一個單利, 判斷系統(tǒng)版本...

1.1.1 不帶參數(shù)的宏定義

//不帶參數(shù)的宏定義
#define 宏名 值
#define COUNT 10
// 提前結(jié)束宏定義的作用域
#undef COUNT
//使用場景一: 定義BASE_URL
#define BASE_URL "http://192.168.1.1/"

1.1.2 帶參數(shù)的宏定義

/*
     #define 代表要定義一個宏
     SUM 宏的名稱
     (v1, v2) 參數(shù), 注意點, 不需要寫數(shù)據(jù)類型
     v1+v2 用于替換的內(nèi)容
     
     宏定義并不會做任何運算, 無論是有參數(shù)還是沒有參數(shù)都僅僅是在翻譯成0和1之前做一個簡單的"替換"
     
     帶參數(shù)的宏定義注意點
     1.一般情況下建議寫帶參數(shù)的宏的時候, 給每個參數(shù)加上一個()
     2.一般情況下建議寫帶參數(shù)的宏的時候, 給結(jié)果也加上一個()
*/
//要求不使用函數(shù), 實現(xiàn)計算兩個變量的和
#define SUM(v1, v2) v1+v2

// 要求定義一個帶參數(shù)的宏, 用于計算兩個變量的乘積
//#define CF(v1, v2) v1*v2
#define CF(v1, v2) (v1)*(v2)//給每個參數(shù)加上一個()

// 要求定義一個帶參數(shù)的宏, 用于計算某個數(shù)的平方
//#define PF(v1) (v1)*(v1)
#define PF(v1) ((v1)*(v1)) //給每個參數(shù)加上一個()疆导,給結(jié)果也加上一個()

注意點: 1.一般情況下建議寫帶參數(shù)的宏的時候, 給每個參數(shù)加上一個(), 給結(jié)果也加上一個()

什么時候用帶參數(shù)的宏定義什么時候用函數(shù)

  • 如果函數(shù)內(nèi)部的功能比較簡單, 僅僅是做一些簡單的運算那么可以使用宏定義, 使用宏定義效率更好, 運算速度更快
  • 如果函數(shù)內(nèi)部的功能比較復(fù)雜, 不僅僅是一些簡單的運算, 那么建議使用函數(shù)

1.2 條件編譯

預(yù)處理指令什么時候執(zhí)行? 編譯之前
變量什么時候定義? 執(zhí)行了才會定義

注意: 條件編譯不能用來判斷變量, 因為不在同一個生命周期, 一般情況下, 條件編譯是和宏定義結(jié)合在一起使用的

條件編譯和選則結(jié)構(gòu)if的異同點

  • 相同點: 都可以對給定的條件進(jìn)行判斷, 添加滿足或者不滿足都可以執(zhí)行特定的代碼
    條件編譯和選擇結(jié)構(gòu)if的共區(qū)別

  • 不同點:

    • 1.生命周期不同, if運行時, #if 編譯之前
    • 2.#if需要一個明確的結(jié)束符號 #endif, 為什么需要一個明確的結(jié)束符號? 如果省略掉#endif, 那么系統(tǒng)就不知道條件編譯的范圍, 那么會將滿足條件之后的第二個條件之后的所有內(nèi)容都清除
    • 3.if會將所有的代碼都編譯到二進(jìn)制中, #if只會將滿足條件的部分一直到下一個條件的部分編譯到二進(jìn)制中

條件編譯的優(yōu)點: 縮小應(yīng)用程序的大小

應(yīng)用場景: 用于調(diào)試和發(fā)布階段進(jìn)行測試

調(diào)試階段: 程序?qū)懘a的階段

發(fā)布階段: 上傳到AppStore的階段

一般在.pch寫以下代碼, 用于自定義輸出語句

#ifdef DEBUG //處于開發(fā)(調(diào)試)階段
#define PCLog(...) NSLog(__VA_ARGS__) //自定義Log
//#define WCLog(...) NSLog(@"%s  %d %@\n\n",__func__,__LINE__,[NSString stringWithFormat:__VA_ARGS__]) //自定義Log, 打印方法名,方法所在行數(shù)葛躏,方法
#else //處于發(fā)布階段
#define PCLog(...)
#endif

條件編譯其它寫法

#ifdef // 判斷是否定義了后面的宏
#elif 
#else
#endif

#ifndef //是不是沒有定義后面的宏,例如.pch文件
#else
#endif

1.3 文件包含

 #include <>

<>會先去編譯器環(huán)境下查找, 找不到再去系統(tǒng)的環(huán)境下查找

 #include ""

""會先在當(dāng)前文件查找, 找不到再去編譯器環(huán)境下查找, 找不到再去系統(tǒng)的環(huán)境下查找

作用: 將""或者<>中的內(nèi)容完全拷貝過來

注意

  • 如何正確的編寫.h文件
  • 如何防止循環(huán)拷貝 A拷貝B, B拷貝A
  • 間接拷貝問題 A拷貝B, B拷貝C, C拷貝D

為了防止重復(fù)導(dǎo)入, 一般情況下會在.h中添加上 頭文件衛(wèi)士

//#ifndef __ZS__H__ // 判斷是否"沒有"定義了名稱叫做 __ZS__H__ 的宏
//#define __ZS__H__ // 定義一個叫做__ZS__H__的宏

// 加法運算
// v1 , v2需要參與運算的數(shù)據(jù)
int sum(int v1, int v2);
//#endif

防止循環(huán)拷貝, A導(dǎo)入B.h #include "B.h", B不導(dǎo)入A.h, 只拷貝A中函數(shù)的聲明即可澈段。

2 typedef


typedef可以給一個已知的數(shù)據(jù)類型起別名

利用typedef給數(shù)據(jù)類型起別名的格式:

typedef 原有的數(shù)據(jù)類型 別名(外號);

注意:

  • 1 typedef不僅能給系統(tǒng)原有的數(shù)據(jù)類型起別名, 也可以給一個自定義的數(shù)據(jù)類型起別名

  • 2 利用typedef給數(shù)據(jù)類型起別名, 并不會生成一個新的數(shù)據(jù)類型, 僅僅是給原有的類型起了一個別名而已

給基本數(shù)據(jù)類型起別名

typedef int Integer;

給結(jié)構(gòu)體類型起別名

// 1.先定義結(jié)構(gòu)體類型, 再給類型起別名
/*
 struct Person
 {
     int age;
     double height;
     char *name;
 };
 // SPerson == struct Person
 typedef struct Person SPerson;
 */

// 2.定義結(jié)構(gòu)體類型的同時, 給結(jié)構(gòu)體類型起別名
/*
 typedef struct Person
 {
     int age;
     double height;
     char *name;
 } SPerson;
 */

// 3.定義結(jié)構(gòu)體類型的同時, 給結(jié)構(gòu)體類型起別名, 并且省略掉原有類型的名稱
 typedef struct
 {
     int age;
     double height;
     char *name;
 } SPerson;

給枚舉類型起別名

//定義枚舉變量有3種方式
//1.先定義枚舉類型, 再定義枚舉變量
//2.定義枚舉類型的同時定義枚舉變量
//3.定義枚舉類型的同時定義枚舉變量, 并且省略枚舉類型名稱

//給枚舉類型起別名也有3種方式
// 1.先定義枚舉類型, 再給枚舉類型起別名
/*
 enum Gender
 {
     kGenderMale,
     kGenderFemale
 };
 typedef enum Gender SEX;
 */

// 2.定義枚舉類型的同時給枚舉類型起別名
/*
 typedef enum Gender
 {
     kGenderMale,
     kGenderFemale
 } SEX;
 */

// 3.定義枚舉類型的同時給枚舉類型起別名, 并且省略枚舉原有類型名稱(最常用方式)
typedef enum
{
    kGenderMale,
    kGenderFemale
} SEX;

給指針起別名 (給block起別名同理)

typedef char * String;
void test4()
{
    // char *name = "pc";
    // 注意: 如果給指針起別名之后, 那么以后利用別名定義變量就不用再加*了
    String name = "pc";
    printf("name = %s\n", name); //name = pc
}
//=============================================================
int sum(int v1, int v2)
{
    return v1 + v2;
}
int minus(int v1, int v2)
{
    return v1 - v2;
}
// 注意: 如果是給指向函數(shù)的指針起別名, 那么指向函數(shù)的指針的指針名稱就是它的別名
// functionPotinter == int(*functionPotinter)(int , int)
typedef int(*functionPotinter)(int , int);
//定義了一個指針類型, 這個類型返回一個int類型的值, 接收兩個int類型的參數(shù). 
//指針可以指向一個"返回int,接收兩個int類型的參數(shù)的函數(shù)"

int main(int argc, const char * argv[]) {
    // 如何定義變量 : 數(shù)據(jù)類型 變量名稱;
    // int (*sumP)(int , int ) = sum;
    functionPotinter sumP = sum;
    printf("sum = %i\n", sumP(10 , 20));
    return 0;
}

3 typedef和宏定義的區(qū)別


一般情況下如果要給數(shù)據(jù)類型起一個名稱建議用typedef, 不要用define

typedef int myInt;
typedef char * String;

4 const關(guān)鍵字


const對基本數(shù)據(jù)類型的作用

const對基本數(shù)據(jù)類型的作用, 可以讓基本數(shù)據(jù)類型的變量變?yōu)槌A?br> const有兩種寫法, 1.寫在數(shù)據(jù)類型的左邊, 2.寫在數(shù)據(jù)類型的右邊

const對指針類型的作用

  • 如果const寫在指針類型的左邊, 那么意味著指向的內(nèi)存空間中的值不能改變, 但是指針的指向可以改變
  • 如果const寫在指針的數(shù)據(jù)類型和*號之間, 那么意味著指向的內(nèi)存空間中的值不能改變, 但是指針的指向可以改變
  • 如果const寫在指針的右邊(數(shù)據(jù)類型 * const), 那么意味著指針的指向不可以改變, 但是指針指向的存儲空間中的值可以改變

int num = 10;
int *p = #
//const int *p = # //*p不能被修改, p能被修改
//int const *p = # //*p不能被修改, p能被修改
//int * const p = #//*p能被修改, p不能被修改
*p = 998; // 修改了指針指向的內(nèi)存空間中存儲的值
printf("&num = %p\n", &num);
printf("p = %p\n", p);
printf("num = %d\n", num);
    
int age = 30;
p = &age; // 修改了指針的指向
printf("&age = %p\n", &age);
printf("p = %p\n", p);

//=============================================================
1.const NSString *PCStr = @"Hello World";
"* PCStr"不能被修改舰攒,"PCStr"能被修改
 
2.NSString const * PCStr = @"Hello World";
"* PCStr"不能被修改败富,"PCStr"能被修改
 
3.NSString * const PCStr = @"Hello World";
"PCStr"不能被修改,"*PCStr"能被修改
 
注意:1和2沒什么區(qū)別

規(guī)律:

  • 如果const寫在指針變量名(p)的旁邊, 那么指針的指向不能變, 而指向的內(nèi)存空間的值可以變
  • 如果const寫在數(shù)據(jù)類型(int)的左邊或者右邊, 那么指針的指向可以改變, 但是指向的內(nèi)存空間的值不能改變

結(jié)論:

  • const右邊的總不能被修改

5 宏定義(define)與常量(const)的選擇


當(dāng)我們想全局共用一些數(shù)據(jù)時摩窃,可以用const, 如何選擇呢兽叮!

  • 編譯時刻: 宏是預(yù)編譯(編譯之前處理),const是編譯階段

  • 編譯檢查: 宏不做檢查猾愿,不會報編譯錯誤鹦聪,只是替換,const會編譯檢查蒂秘,會報編譯錯誤泽本。

  • 宏的好處: 宏能定義一些函數(shù),方法姻僧。 const不能规丽。

  • 宏的壞處: 使用大量宏蒲牧,容易造成編譯時間久,每次都需要重新替換嘁捷。

  • const: 共享一塊內(nèi)存空間造成,就算項目中N處用到,也不會分配N塊內(nèi)存空間雄嚣,可以根據(jù)const修飾的位置設(shè)定能否修改晒屎,在編譯階段會執(zhí)行類型檢查

蘋果推薦使用const常量。

定義全局常量, 一般會寫在獨立文件里

PCConst.h文件

#import <Foundation/Foundation.h>

// cell的列數(shù)
extern NSInteger * const PCCellColumn;
//extern與const組合:只需要定義一份全局變量缓升,多個文件共享鼓鲁。

// 刪除文字的通知
extern NSString * const PCWordsDidDeleteNotification;

PCConst.m文件

#import "PCConst.h"

// cell的列數(shù)
NSInteger * const PCCellColumn = 3;

// 刪除文字的通知
NSString * const PCWordsDidDeleteNotification = @"PCWordsDidDeleteNotification";
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市港谊,隨后出現(xiàn)的幾起案子骇吭,更是在濱河造成了極大的恐慌,老刑警劉巖歧寺,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件燥狰,死亡現(xiàn)場離奇詭異,居然都是意外死亡斜筐,警方通過查閱死者的電腦和手機(jī)龙致,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顷链,“玉大人目代,你說我怎么就攤上這事∴土罚” “怎么了榛了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長煞抬。 經(jīng)常有香客問我霜大,道長,這世上最難降的妖魔是什么革答? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任僧诚,我火速辦了婚禮,結(jié)果婚禮上蝗碎,老公的妹妹穿的比我還像新娘。我一直安慰自己旗扑,他們只是感情好蹦骑,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著臀防,像睡著了一般眠菇。 火紅的嫁衣襯著肌膚如雪边败。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天捎废,我揣著相機(jī)與錄音笑窜,去河邊找鬼。 笑死登疗,一個胖子當(dāng)著我的面吹牛排截,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辐益,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼断傲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了智政?” 一聲冷哼從身側(cè)響起认罩,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎续捂,沒想到半個月后垦垂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡牙瓢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年劫拗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片一罩。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡杨幼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聂渊,到底是詐尸還是另有隱情差购,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布汉嗽,位于F島的核電站欲逃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏饼暑。R本人自食惡果不足惜稳析,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弓叛。 院中可真熱鬧彰居,春花似錦、人聲如沸撰筷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毕籽。三九已至抬闯,卻和暖如春井辆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溶握。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工杯缺, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人睡榆。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓萍肆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肉微。 傳聞我的和親對象是個殘疾皇子匾鸥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容