1.5.3 參數(shù)的合法性檢查
很多細(xì)致的程序員會在每個函數(shù)的入口處檢查所有傳入?yún)?shù)的合法性,尤其是指針。比如,下面的函數(shù)會銷毀一個映射表:
int pcutils_map_destroy(pcutils_map* map) { if (map == NULL) return -1; pcutils_map_clear(map); free(map); return 0; }
該函數(shù)首先判斷傳入的參數(shù)map
是否為空指針。可以預(yù)期,傳入該函數(shù)的參數(shù)map
是由名為pcutils_map_create()
的函數(shù)返回的。作為創(chuàng)建對象的函數(shù)接口,一般返回空值(NULL
指針)表示失敗,返回非空值則表示成功;如果pcutils_map_create()
函數(shù)返回空值,則不用再調(diào)用pcutils_map_destroy()
函數(shù)。換句話說,在調(diào)用pcutils_map_destroy()
函數(shù)時,除非誤用,否則不會給這個函數(shù)傳遞一個空值。
因此,這種判斷貌似有必要,但仔細(xì)考慮后就會發(fā)現(xiàn)意義不大。在上面的代碼中,程序?qū)?code>NULL作為非法值做了特別處理,但如果傳入的指針值為1或者?1,它們顯然也是非法值,那為何不對這兩種情況做判斷并返回對應(yīng)的錯誤值呢?更進(jìn)一步地,如何判斷一個尚未分配的地址值呢?
實質(zhì)上,C語言并沒有提供任何能夠判斷一個指針的值是否合法的語言級能力或者機(jī)制。我們所知道的不合法的指針值通常就是0、?1,以及特定情況下和當(dāng)前處理器的位寬不對齊的整數(shù)值。比如在32位系統(tǒng)中,對于指向32位整數(shù)的指針來講,任何不能被4整除的指針值大概率是非法的。除此之外,我們沒有其他有效的手段來判斷一個指針值的合法性。因此,這類參數(shù)的有效性檢查其實是多余的。
再者,在頻繁調(diào)用的函數(shù)中執(zhí)行此類不必要的參數(shù)有效性檢查,會大大降低程序的執(zhí)行效率。
因此,上述代碼的最佳實現(xiàn)應(yīng)該如下:
void pcutils_map_destroy(pcutils_map* map) { pcutils_map_clear(map); free(map); }
我們沒有必要僅針對空值做參數(shù)的有效性檢查。一方面,這種檢查并不能覆蓋所有的情形;另一方面,如果我們僅僅需要檢查空值這種情形,那么程序會很快因為訪問空指針而出錯。后一種情況說明調(diào)用者誤傳了參數(shù),在程序的開發(fā)階段,借助調(diào)試器,我們可以迅速定位缺陷所在。
但在某些情況下,我們?nèi)匀幌M谡{(diào)用這類函數(shù)時,對傳入的常見非法值NULL
做一些特殊處理,以便可以及時發(fā)現(xiàn)調(diào)用者的問題。為此,我們可以使用assert()
。assert()
本質(zhì)上是一個宏,而非函數(shù),而且這個宏的行為依賴于NDEBUG
宏。assert()
通常的定義如下:
#ifdef NDEBUG # define assert(exp) \ do { \ } while (0) #else /* defined NDEBUG */ # define assert(exp) \ do { \ if (!(exp)) \ abort(); \ } while (0) #endif /* not defined NDEBUG */
在上面的代碼中,NDEBUG
是一個約定俗成的全局宏,通常由構(gòu)建系統(tǒng)定義。當(dāng)NDEBUG
宏被定義時,意味著程序?qū)⒈粯?gòu)建為發(fā)布版本,assert()
不做任何事情;反之,當(dāng)程序被構(gòu)建為調(diào)試版本時,assert()
將判斷表達(dá)式exp
的真假,若為假,則調(diào)用abort()
函數(shù)終止程序的運行。
如此一來,我們可以將上述代碼進(jìn)一步修改為如下形式:
#include <assert.h> void pcutils_map_destroy(pcutils_map* map) { assert(map != NULL); pcutils_map_clear(map); free(map); }
此外,還有一種針對參數(shù)的合法性檢查,或者說針對常規(guī)條件分支的優(yōu)化方法,常見于一些優(yōu)秀的C語言開源項目中。程序清單1.4列出了glib(Linux系統(tǒng)常用的C工具函數(shù)庫,在一些場景中也可寫作GLib)中用于快速驗證UTF-8編碼有效性的函數(shù)。
程序清單1.4 使用UNLIKELY
宏優(yōu)化條件分支
#define VALIDATE_BYTE(mask, expect) \ do { \ if (UNLIKELY((*(uint8_t *)p & (mask)) != (expect))) \ goto error; \ } while (0) /* see IETF RFC 3629 Section 4 */ static const char * fast_validate(const char *str) { size_t n = 0; const char *p; for (p = str; *p; p++) { if (*(uint8_t *)p < 128) { n++; } else { const char *last; last = p; if (*(uint8_t *)p < 0xe0) { /* 110xxxxx */ if (UNLIKELY (*(uint8_t *)p < 0xc2)) goto error; } else { if (*(uint8_t *)p < 0xf0) { /* 1110xxxx */ switch (*(uint8_t *)p++ & 0x0f) { ... } } else if (*(uint8_t *)p < 0xf5) { /* 11110xxx excluding out-of-range */ switch (*(uint8_t *)p++ & 0x07) { ... } p++; VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */ } else goto error; } p++; VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */ n++; continue; error: return last; } } return p; }
上述代碼多次使用了UNLIKELY
宏,用于判斷一些不太可能出現(xiàn)在正常UTF-8編碼中的字符。這個宏以及成對定義的LIKELY
宏利用了現(xiàn)代編譯器的一些特性,它們可以告訴編譯器一個分支判斷的結(jié)果為真或者為假的可能性是大還是小。利用這兩個宏,我們可以協(xié)助編譯器充分利用處理器的分支預(yù)測能力,提高編譯后代碼的執(zhí)行效率。
因此,如果非要檢查傳入?yún)?shù)的有效性,我們可以利用UNLIKELY
宏,對旨在銷毀映射表的代碼作如下優(yōu)化:
int pcutils_map_destroy(pcutils_map* map) { if (UNLIKELY(map == NULL)) return -1; pcutils_map_clear(map); free(map); return 0; }
這樣編譯器就會認(rèn)為出現(xiàn)map == NULL
這一條件的可能性較低,從而在生成最終的機(jī)器指令時,通過適當(dāng)?shù)膬?yōu)化,將可能性較低的條件判斷對性能的影響降到最小。
注意,LIKELY
和UNLIKELY
宏是非標(biāo)準(zhǔn)宏,目前僅GCC或兼容GCC的編譯器支持。這兩個宏通常定義如下:
/* LIKELY */ #if !defined(LIKELY) && defined(__GNUC__) #define LIKELY(x) __builtin_expect(!!(x), 1) #endif #if !defined(LIKELY) #define LIKELY(x) (x) #endif /* UNLIKELY */ #if !defined(UNLIKELY) && defined(__GNUC__) #define UNLIKELY(x) __builtin_expect(!!(x), 0) #endif #if !defined(UNLIKELY) #define UNLIKELY(x) (x) #endif
其中使用了__builtin_expect
這一GCC特有的優(yōu)化指令。
- Java范例大全
- Java入門很輕松(微課超值版)
- 軟件測試項目實戰(zhàn)之性能測試篇
- 精通軟件性能測試與LoadRunner實戰(zhàn)(第2版)
- Visual Basic程序設(shè)計教程
- Hadoop+Spark大數(shù)據(jù)分析實戰(zhàn)
- Linux環(huán)境編程:從應(yīng)用到內(nèi)核
- Expert Android Programming
- PHP+MySQL+Dreamweaver動態(tài)網(wǎng)站開發(fā)實例教程
- AutoCAD VBA參數(shù)化繪圖程序開發(fā)與實戰(zhàn)編碼
- Responsive Web Design by Example
- 信息技術(shù)應(yīng)用基礎(chǔ)
- 深入淺出Serverless:技術(shù)原理與應(yīng)用實踐
- Scala編程實戰(zhàn)(原書第2版)
- Python Web數(shù)據(jù)分析可視化:基于Django框架的開發(fā)實戰(zhàn)