ctype.h和ctype.c中的實現方式與編碼藝術

在C語言中,有這樣幾個函數: linux

int isalnum(c)  //檢查c是不是字母或數字
int isalpha(c)  //檢查c是不是字母
int iscntrl(c)  // 檢查c是否控制字符(其ASCII碼在0和0x1F之間,數值爲 0-31)
int isdigit(c)  //檢測是不是數字
int isgraph(c)  //檢查c是否可顯示字符(其ASCII碼在ox21到ox7E之間),不包括空格
int islower(c) 
int isprint(c)  //檢查c是不是可打印字符(包括空格),其ASCII碼在ox20到ox7E之間 
int ispunct(c) //檢查c是不是標點字符(不包括空格),即除字母,數字和空格之外的全部可打印字符 
int isspace(c)  //檢查c是不是空格符和跳格符(控制字符)或換行符
int isupper(c)
int isxdigit(c)  //檢查c是不是一個16進制數學字符(即0-9,或A-F,或a-f)

通常人的實現方法是用宏定義的方法來實現這寫函數,好比對於int isdigit()函數: git

#define isdigit(c) ((c)>=’0’&&(c)<=’9’)

這樣定義使函數簡潔,使用宏定義省掉了函數調用的開銷,提升了效率。 數組

咱們來看看linux系統下是如何實現的: 函數

#define _U	0x01	/* upper */
#define _L	0x02	/* lower */
#define _D	0x04	/* digit */
#define _C	0x08	/* cntrl */
#define _P	0x10	/* punct */
#define _S	0x20	/* white space (space/lf/tab) */
#define _X	0x40	/* hex digit */
#define _SP	0x80	/* hard space (0x20) */

extern unsigned char _ctype[];
extern char _ctmp;

#define isdigit(c) ((_ctype+1)[c]&(_D)) 

unsigned char _ctype[] = {0x00,			/* EOF */
_C,_C,_C,_C,_C,_C,_C,_C,			/* 0-7 */
_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,		/* 8-15 */
_C,_C,_C,_C,_C,_C,_C,_C,			/* 16-23 */
_C,_C,_C,_C,_C,_C,_C,_C,			/* 24-31 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P,			/* 32-39 */
_P,_P,_P,_P,_P,_P,_P,_P,			/* 40-47 */
_D,_D,_D,_D,_D,_D,_D,_D,			/* 48-55 */
_D,_D,_P,_P,_P,_P,_P,_P,			/* 56-63 */
_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U,	/* 64-71 */
_U,_U,_U,_U,_U,_U,_U,_U,			/* 72-79 */
_U,_U,_U,_U,_U,_U,_U,_U,			/* 80-87 */
_U,_U,_U,_P,_P,_P,_P,_P,			/* 88-95 */
_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L,	/* 96-103 */
_L,_L,_L,_L,_L,_L,_L,_L,			/* 104-111 */
_L,_L,_L,_L,_L,_L,_L,_L,			/* 112-119 */
_L,_L,_L,_P,_P,_P,_P,_C,			/* 120-127 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 128-143 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 144-159 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 160-175 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 176-191 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 192-207 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 208-223 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 224-239 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};		/* 240-255 */
    這種方法是用映射的方法,將 ASCII 碼值映射到 _ctype 數組中的每一項。例如 字符‘0~9’對應的ASCII碼爲48~57,映射到上面的_ctype數組相應的位置全是_D_D&_D爲真,這樣就能判斷出參數c的值是否是數字字符。

另外我在這裏時對於#define isdigit(c) ((_ctype+1)[c]&(_D)) 函數中爲何有_ctype+1不瞭解,這裏若_ctype+1則指向數組的第二個元素_C,爲何要這樣呢? 編碼

這是由於linux大師們在定義字符時把EOF也定義進去了,而EOF的值爲0,多定義了這個元素,並且把他定義在第一位,因此要跳過這一位而從第一個元素開始。還有就是NULL/0其實也是一個控制字符,因此在int
iscntrl(c)
函數中若是傳進實參爲NULL,返回值是1的。 spa

我在VS2008裏進行試驗,其代碼爲: 指針

char word[]="chengdu";
printf("%c",(word+1)[1]);
     結果顯示爲「e」。也就是說,(word+1)做爲一個指針指向word字符串的第二個值。或者說,在數組中,數組名其實就是一個指針,其驗證方法以下:
char word[]="chengdu";
printf("%c",*(word+1));

顯示結果爲「h」。 code

        Linux中這幾個函數典型的運用了空間換時間的辦法,其精華之處在於,對不一樣種類的字符進行了分類,並使用惟一的二進制來進行標識。這些用法相信通常的人是想不出來的,而這也正體現出大師們的大師之處。看來有時間仍是要多看linux內核的代碼,從中領悟出大師們的編碼藝術思想。 orm

相關文章
相關標籤/搜索