注意以寫的文章,全部理論都是基於類Unix系統的C程序編寫。linux
好的程序即便切換到任意操做系統平臺並使用任意的編譯器編譯,也不該該出現太多的警告,更不能出現編譯不經過的狀況了。編程
//平臺檢測 #if !defined(__LINUX__) && (defined(__linux__) || defined(__KERNEL__) \ || defined(_LINUX) || defined(LINUX) || defined(__linux)) #define __LINUX__ (1) #elif !defined(__APPLE__) && (defined(__MacOS__) || defined(__apple__)) #define __APPLE__ (1) #elif !defined(__CYGWIN__) && (defined(__CYGWIN32__) || defined(CYGWIN)) #define __CYGWIN__ (1) #elif !defined(__WINDOWS__) && (defined(_WIN32) || defined(WIN32) \ || defined(_window_) || defined(_WIN64) || defined(WIN64)) #define __WINDOWS__ (1) #elif !(defined(__LINUX__) || defined(__APPLE__) \ || defined(__CYGWIN__) || defined(__WINDOWS__)) #error "`not support this platform`" #endif
#ifdef __LINUX__ #include <sys/epoll.h> #endif
//編譯器版本 /* gcc version. for example : v4.1.2 is 40102, v3.4.6 is 30406 */ #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
編譯器特性多線程
本人不是很厲害,如今僅僅使用下面兩個頗有用的特性。app
printf()
類函數輸入的參數是否合法,這樣能夠在編譯時就發出警告,而不至於由於格式錯誤發生運行時錯誤,那麼可使用以下編譯器提供的特性://字符串格式化參數檢查 #if !defined(__printflike) && defined(GCC_VERSION) #define __printflike(fmtarg, firstvararg) __attribute__((__format__(__printf__, fmtarg, firstvararg))) #endif
//邏輯跳轉優化 #if GCC_VERSION /*條件大多數爲真,與if配合使用,直接執行if中語句*/ #define likely(x) __builtin_expect(!!(x), 1) /*條件大多數爲假,與if配合使用,直接執行else中語句*/ #define unlikely(x) __builtin_expect(!!(x), 0) #else #define likely(x) (!!(x)) #define unlikely(x) (!!(x)) #endif
使用編譯器特性進行編程函數
printf()
的使用方式;在函數聲明後跟__printflike(6,7) 表面咱們要檢查類格式化函數的格式字符串從第六個參數開始,而被格式化的變參從第七個參數開始,以下:之後我討論個人這套日誌輸出系統,這裏的聲明中函數有些不一樣,之後我會說明。優化
// 寫日誌 // @param cfg 日誌配置數據 // @param level 當前須要打印的日誌級別 // @param func 該條日誌的輸出所在函數名 // @param file 該條日誌的輸出所在文件名 // @param line 該條日誌的輸出所在文件行 // @param formate 該條日誌輸出的格式 // @param ... 依據格式輸出的邊參列表 // @return 0 成功; -1 失敗
int (SLogWrite)(SLogCfgT *cfg, const SLogLevelT *level, const char *func, const char *file, int line, const char *formate, ...) __printflike(6, 7); //正確的使用方式 ... /*默認日誌級別定義表/ extern const SLogLevelT g_slogLevels[]; /*默認日誌配置/ extern SLogCfgT g_slogCfg; (SLogWrite)(&g_slogCfg, &g_slogLevels[1], func, file, line, "%s", "test macro of __printflike(x, y)"); //錯誤的使用方式 (SLogWrite)(&g_slogCfg, &g_slogLevels[1], func, file, line, "%d", "test macro of __printflike(x, y)");ui
第一條調用不會發生警告,而第二條會發生警告,本來是但願將變參看成整數處理,但是卻傳入了一個字符串,這會產生一條編譯警告,而警告可能會使程序運行產生未知行爲,擔任有時候也不會出什麼問題,但有時會使程序蹦掉,因此不能存在僥倖心理。 - 條件預判斷。幾乎全部的C程序都須要檢查變量的合法性,好比咱們定義了一個結構體,可是可能忘記初始化結構體了,若是這樣交給函數處理,可能發送未知行爲,而致使錯誤的結果,甚至發生程序蹦掉。以下: ```c ... struct T { void *data; ... }; void deal(struct T *ptr) { int *data = (int*)ptr->data; printf("%d\n", *data); } struct T data; deal(&data); ...
若是未初始化的data.data
指向的是一個不可讀寫的指針地址,那麼在deal()
中解引用必定會使程序引起段錯誤而蹦掉。下面咱們定義一系列宏,進行判斷,可是發生錯誤的狀況是甚少的,因此爲效率考慮,咱們在宏中使用likely()
條件預判斷:this
//魔數 // 結構體中設置一個magic的成員變量,以檢查結構體是否被正確初始化 #if !defined(OBJMAGIC) #define OBJMAGIC (0xfedcba98) //設置魔術 #define REFOBJ(obj) \ ({ \ bool _ret = false; \ if (likely((obj))) { \ (obj)->magic = OBJMAGIC; \ _ret = true; \ } \ _ret; \ }) //重置魔數 #define UNREFOBJ(obj) \ ({ \ bool _ret = false; \ if (likely((obj) && \ (obj)->magic == OBJMAGIC)) { \ (obj)->magic = 0; \ _ret = true; \ } \ _ret; \ }) //驗證魔數 #define ISOBJ(obj) \ (likely((obj) && (obj)->magic == OBJMAGIC)) //斷言魔數 #define ASSERTOBJ(obj) \ (assert(ISOBJ((obj)))) #endif /* if !defined(OBJMAGIC) */
使用以下:操作系統
struct T { int magic; void *data; ... }; void deal(struct T *ptr) { if(!ISOBJ(ptr)) return; int *data = (int*)ptr->data; printf("%d\n", *data); } void initobj(struct T *ptr, void *data) { if(!REFOBJ(&data) || !data) return; ptr->data = data; } ... struct T data; int value = 1; initobj(&data, (void*)&value); //下個初始化不會再次初始化結構體 initobj(&data, (void*)&value); deal(&data);
雖然每一個結構體必需要保留一個magic這個字段,會佔用一些時間和空間,但這樣作是值得的,至少你能夠少些不少代碼來證實這個結構體被正確的初始化過,能夠放心的使用。你應該保證你的初始化函數是預期的執行過。固然你也可使用ASSERTOBJ ()
判斷結構體,在發現未初始化後,立馬abort()
程序,而後根據日誌糾正錯誤,這至少比莫名的段錯誤要好不少。C就是這樣,你要作不少工做來保證你的程序的正確性,這個是你的職責所在,不能嫌麻煩。關鍵是你要以何種靈活簡單高效的方法來作這個事情。線程
在多核cpu流行的今天,多線程編程是必不可少的技能,而在原子操做是多線程裏佔很重要的地位,下一節咱們將討論原子操做的運行,並用原子操做實現一個比較高級的同步鎖,該鎖能很好的檢查死鎖,並將其修復,並且速度比起POSIX的互斥量快不少,而切比起自旋鎖也多了檢查死鎖和能夠遞歸操做的特性。