ctype.hとctype.cにおける実現方式と符号化芸術


C言語には、次のような関数があります.
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()関数などのマクロ定義方法でこの書き込み関数を実装することです.
#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は配列の2番目の要素を指す_C、どうしてこんなことになったの?
これはlinuxマスターたちが文字を定義するときにEOFも定義したためで、EOFの値は0で、この要素を多く定義し、彼を1位に定義したので、この1位をスキップして最初の要素から始めます.また、NULL/0も実は制御文字なので、int iscntrl(c)関数で実パラメータNULLが伝達されると、戻り値は1になります.
私はVS 2008で実験を行いました.そのコードは:
char word[]="chengdu";
printf("%c",(word+1)[1]);
の結果は「e」と表示されます.すなわち,(word+1)は1つのポインタとしてword文字列の2番目の値を指す.あるいは、配列では、配列名はポインタであり、その検証方法は以下の通りです.
char word[]="chengdu";
printf("%c",*(word+1));

結果を「h」と表示します.
Linuxのこれらの関数は典型的に空間を時間に変える方法を用いており,その精華は,異なる種類の文字を分類し,唯一のバイナリを用いて識別することである.これらの使い方は一般の人は考えられないと信じていますが、これもマスターたちのマスターのところを体現しています.時間があればlinuxカーネルのコードをもっと見て、マスターたちのコード芸術思想を悟る必要があるようです.