預處理指令:
程序猿編寫的代碼不是標準C代碼,并不能被真正的編譯器索編譯,需要一段程序把代碼翻譯一下。
翻譯的過程叫做預處理,被翻譯的代碼叫做預處理指令,以#開頭的都是預處理指令查看預處理的過程:gcc -E code.c 把預處理結果顯示在終端上gcc -E code.c -o code.i 把預處理的結果存儲到code.i文件中預處理指令的分類:#inlcude 文件包含#include<> 從系統指定目錄下查找并導入頭文件#include"" 先從文件當前目錄下找,找到就導入該頭文件;如果找不到,再從系統指定目錄下找并導入頭文件操作系統通過環境變量來指定頭文件的查找路徑,或者通過設置編譯參數來指定頭文件的查找路徑-I/path.bashrc#define 宏定義宏常量: #define MAX 100優點:提高代碼的擴展性(方便批量修改)、提高可讀性、提高安全性、還可以在case后面使用注意:一般宏名全部大寫,末尾不要加分號【局部變量全部小寫、全局變量首字母大寫、指針變量+p、數組arr、字符串str、函數名全部小寫+下劃線】預定義好的宏:__func__ 獲取函數名__FILE__ 獲取文件名__LINE__ 獲取當前行號__DATE__ 獲取當前日期__TIME__ 獲取運行時間宏函數: 其實就是帶參數的宏宏函數不是真正的函數,不檢查參數類型,沒有傳參,沒有返回值,只有計算的結果#define sum(a,b) a+b1、把代碼使用到的宏函數替換為宏函數后面的代碼。2、再把宏函數代碼中使用到的參數替換為調用者提供的參數宏函數的二義性:由于宏代碼所處的位置、參數不同導致宏有不同的功能,這就叫做宏的二義性。如何避免宏的二義性:1、宏函數整體加小括號,每個參數都加小括號2、使用宏函數時,不要提供帶自變運算符的變量作為參數注意1:容易出選擇題,例如:哪個宏有二義性、宏函數的運算結果注意2:定義宏盡量別換行,如果要換行要在每行末尾加上續航符\,建議宏函數最外面加上大括號運算符:# 把宏函數的參數變成字符串## 合并兩個參數變成一個標識符普通函數與宏函數的區別?它們是什么:普通函數:是一段具有某項功能的代碼段,會被編譯成二進制指令存儲到代碼段內存中,函數名就是首地址,有獨立的命名空間、棧內存宏函數:是一個帶參數的宏,并不是真正的函數,而只是代碼的替換,僅僅只是使用起來像函數有什么不一樣:函數: 返回值 類型檢查 安全 壓棧、出棧 速度慢 跳轉宏函數: 運算結果 通用 危險 替換 速度快 冗余條件編譯:根據條件決定那些代碼是否參與最終的編譯版本控制:#if#elif#else#endif頭文件衛士:防止頭文件被重復包含#ifndef 宏名#define 宏名#endif 判斷調試:#ifdef 宏名(DEBUG)#else#endif用于輸出調試信息:#ifdef DEBUG#define debug(...) printf(__VA_ARGS__)#else #define debug(...)#endif#define error(...) printf("%s:%s:%d %s:%m %s %s\n",__FILE__,__func__,__LINE__,__VA_ARGS__,__DATE__,__TIME__)
頭文件應該怎么寫:
問題:頭文件可能被任意個源文件包含,意味著頭文件中的內容會在多個目標文件中存在,合并時不能有沖突。
重點:頭文件中只能編寫聲明語句,不能有定義語句全局變量聲明 extern int num;函數聲明 宏常量宏函數typedef 類型重定義結構、聯合、枚舉的類型聲明
頭文件的編寫原則:
1、為每一個.c文件編寫一份.h文件,.h文件是對.c文件的說明
2、如果需要用到某個.c文件中的函數、變量、宏,只需要把它的頭文件導入
3、.c也需要導入它的.h文件,目的是為了讓聲明和定義一致
頭文件的相互包含:
假如a.h包含了b.h,b.h有需要包含a.h,這種情況叫做頭文件的相互包含,這種情況就會編譯出錯。
錯誤:未知類型名錯誤“xxx”,一般都是因為頭文件相互包含導致的(還可能是復制文件時粗心,忘記改宏名)
解決方案:把a.h中需要的內容,和b.h中需要的內容提取出來,編寫一個c.h
注意:頭文件的相互包含和重復包含的區別