目錄
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)制中
- 1.生命周期不同,
條件編譯的優(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";