默默觉得原来的阅读笔记的名字太土了,改了个名字,叫做走进C标准库。
自己就是菜鸟一只,第一次具体看C标准库,文章参杂了对《the standard C library》的阅读和对源码的一些个人浅显理解,自己记录一下,日后有机会来看可能有另一番感悟吧。
assert.h
assert宏定义的两种表达方式:
#define assert(exp) ((exp) ? (void)0 : _assert(msg))
#define assert(exp) (void)( (exp) || _assert(msg))
在《C陷阱与缺陷》一书中有描述关于assert宏实现上的考虑。
在实现上,我们抛弃了下面这种宏实现的方式:
#define assert(exp) if(!exp) _assert(msg);
因为上述这个宏可能会产生某些难于察觉的错误:
if(x > 0 && y > 0) assert(x > y); else assert(y > x);
上述代码如果采用该宏就会产生else悬挂的问题,且无法在宏内合理有效消除,很不直观。
所以,采用一开始的两种assert的宏定义是相对较好的。
ctype.h
1. 发展历程:
惯用法( if (0 <= c && c <= '9' ) ,使用频繁程序过长且没有利用好重用的代码) 到
函数区分字符类别( isalpha(c) 调用次数过频,影响了程序的执行时间 ) 到
宏定义(节约了执行时间,但是会遇到一些问题)
2. 宏定义可能会产生的问题
3. 使用转换表
字符c编入以_ctype命名的转换表索引中。每个表项的不同位以索引字符为特征。如果任何一个和掩码_XXXMARK相对应的位被设置了,那个字符就在测试的类别中。对所有正确的参数,宏展开成一个紧凑的非零表达式。
1 #define _UPPER 0x1 /* upper case letter */ 2 #define _LOWER 0x2 /* lower case letter */ 3 #define _DIGIT 0x4 /* digit[0-9] */ 4 #define _SPACE 0x8 /* tab, carriage return, newline, */ 5 /* vertical tab or form feed */ 6 #define _PUNCT 0x10 /* punctuation character */ 7 #define _CONTROL 0x20 /* control character */ 8 #define _BLANK 0x40 /* space char */ 9 #define _HEX 0x80 /* hexadecimal digit */ 10 11 #define _LEADBYTE 0x8000 /* multibyte leadbyte */ 12 #define _ALPHA (0x0100|_UPPER|_LOWER) /* alphabetic character */
1 unsigned short *_pctype = _ctype+1; /* pointer to table for char's */ 2 unsigned short *_pwctype = _ctype+1; /* pointer to table for wchar_t's */ 3 4 unsigned short _ctype[257] = { 5 0, /* -1 EOF */ 6 _CONTROL, /* 00 (NUL) */ 7 _CONTROL, /* 01 (SOH) */ 8 _CONTROL, /* 02 (STX) */ 9 _CONTROL, /* 03 (ETX) */ 10 _CONTROL, /* 04 (EOT) */ 11 _CONTROL, /* 05 (ENQ) */ 12 _CONTROL, /* 06 (ACK) */ 13 _CONTROL, /* 07 (BEL) */ 14 _CONTROL, /* 08 (BS) */ 15 _SPACE+_CONTROL, /* 09 (HT) */ 16 _SPACE+_CONTROL, /* 0A (LF) */ 17 _SPACE+_CONTROL, /* 0B (VT) */ 18 _SPACE+_CONTROL, /* 0C (FF) */ 19 _SPACE+_CONTROL, /* 0D (CR) */ 20 _CONTROL, /* 0E (SI) */ 21 _CONTROL, /* 0F (SO) */ 22 _CONTROL, /* 10 (DLE) */ 23 _CONTROL, /* 11 (DC1) */ 24 _CONTROL, /* 12 (DC2) */ 25 _CONTROL, /* 13 (DC3) */ 26 _CONTROL, /* 14 (DC4) */ 27 _CONTROL, /* 15 (NAK) */ 28 _CONTROL, /* 16 (SYN) */ 29 _CONTROL, /* 17 (ETB) */ 30 _CONTROL, /* 18 (CAN) */ 31 _CONTROL, /* 19 (EM) */ 32 _CONTROL, /* 1A (SUB) */ 33 _CONTROL, /* 1B (ESC) */ 34 _CONTROL, /* 1C (FS) */ 35 _CONTROL, /* 1D (GS) */ 36 _CONTROL, /* 1E (RS) */ 37 _CONTROL, /* 1F (US) */ 38 _SPACE+_BLANK, /* 20 SPACE */ 39 _PUNCT, /* 21 ! */ 40 _PUNCT, /* 22 " */ 41 _PUNCT, /* 23 # */ 42 _PUNCT, /* 24 $ */ 43 _PUNCT, /* 25 % */ 44 _PUNCT, /* 26 & */ 45 _PUNCT, /* 27 ' */ 46 _PUNCT, /* 28 ( */ 47 _PUNCT, /* 29 ) */ 48 _PUNCT, /* 2A * */ 49 _PUNCT, /* 2B + */ 50 _PUNCT, /* 2C , */ 51 _PUNCT, /* 2D - */ 52 _PUNCT, /* 2E . */ 53 _PUNCT, /* 2F / */ 54 _DIGIT+_HEX, /* 30 0 */ 55 _DIGIT+_HEX, /* 31 1 */ 56 _DIGIT+_HEX, /* 32 2 */ 57 _DIGIT+_HEX, /* 33 3 */ 58 _DIGIT+_HEX, /* 34 4 */ 59 _DIGIT+_HEX, /* 35 5 */ 60 _DIGIT+_HEX, /* 36 6 */ 61 _DIGIT+_HEX, /* 37 7 */ 62 _DIGIT+_HEX, /* 38 8 */ 63 _DIGIT+_HEX, /* 39 9 */ 64 _PUNCT, /* 3A : */ 65 _PUNCT, /* 3B ; */ 66 _PUNCT, /* 3C < */ 67 _PUNCT, /* 3D = */ 68 _PUNCT, /* 3E > */ 69 _PUNCT, /* 3F ? */ 70 _PUNCT, /* 40 @ */ 71 _UPPER+_HEX, /* 41 A */ 72 _UPPER+_HEX, /* 42 B */ 73 _UPPER+_HEX, /* 43 C */ 74 _UPPER+_HEX, /* 44 D */ 75 _UPPER+_HEX, /* 45 E */ 76 _UPPER+_HEX, /* 46 F */ 77 _UPPER, /* 47 G */ 78 _UPPER, /* 48 H */ 79 _UPPER, /* 49 I */ 80 _UPPER, /* 4A J */ 81 _UPPER, /* 4B K */ 82 _UPPER, /* 4C L */ 83 _UPPER, /* 4D M */ 84 _UPPER, /* 4E N */ 85 _UPPER, /* 4F O */ 86 _UPPER, /* 50 P */ 87 _UPPER, /* 51 Q */ 88 _UPPER, /* 52 R */ 89 _UPPER, /* 53 S */ 90 _UPPER, /* 54 T */ 91 _UPPER, /* 55 U */ 92 _UPPER, /* 56 V */ 93 _UPPER, /* 57 W */ 94 _UPPER, /* 58 X */ 95 _UPPER, /* 59 Y */ 96 _UPPER, /* 5A Z */ 97 _PUNCT, /* 5B [ */ 98 _PUNCT, /* 5C \ */ 99 _PUNCT, /* 5D ] */ 100 _PUNCT, /* 5E ^ */ 101 _PUNCT, /* 5F _ */ 102 _PUNCT, /* 60 ` */ 103 _LOWER+_HEX, /* 61 a */ 104 _LOWER+_HEX, /* 62 b */ 105 _LOWER+_HEX, /* 63 c */ 106 _LOWER+_HEX, /* 64 d */ 107 _LOWER+_HEX, /* 65 e */ 108 _LOWER+_HEX, /* 66 f */ 109 _LOWER, /* 67 g */ 110 _LOWER, /* 68 h */ 111 _LOWER, /* 69 i */ 112 _LOWER, /* 6A j */ 113 _LOWER, /* 6B k */ 114 _LOWER, /* 6C l */ 115 _LOWER, /* 6D m */ 116 _LOWER, /* 6E n */ 117 _LOWER, /* 6F o */ 118 _LOWER, /* 70 p */ 119 _LOWER, /* 71 q */ 120 _LOWER, /* 72 r */ 121 _LOWER, /* 73 s */ 122 _LOWER, /* 74 t */ 123 _LOWER, /* 75 u */ 124 _LOWER, /* 76 v */ 125 _LOWER, /* 77 w */ 126 _LOWER, /* 78 x */ 127 _LOWER, /* 79 y */ 128 _LOWER, /* 7A z */ 129 _PUNCT, /* 7B { */ 130 _PUNCT, /* 7C | */ 131 _PUNCT, /* 7D } */ 132 _PUNCT, /* 7E ~ */ 133 _CONTROL, /* 7F (DEL) */ 134 /* and the rest are 0... */ 135 };
1 #define isalpha(_c) ( _pctype[_c] & (_UPPER|_LOWER) ) 2 #define isupper(_c) ( _pctype[_c] & _UPPER ) 3 #define islower(_c) ( _pctype[_c] & _LOWER ) 4 #define isdigit(_c) ( _pctype[_c] & _DIGIT ) 5 #define isxdigit(_c) ( _pctype[_c] & _HEX ) 6 #define isspace(_c) ( _pctype[_c] & _SPACE ) 7 #define ispunct(_c) ( _pctype[_c] & _PUNCT ) 8 #define isalnum(_c) ( _pctype[_c] & (_UPPER|_LOWER|_DIGIT) ) 9 #define isprint(_c) ( _pctype[_c] & (_BLANK|_PUNCT|_UPPER|_LOWER|_DIGIT) ) 10 #define isgraph(_c) ( _pctype[_c] & (_PUNCT|_UPPER|_LOWER|_DIGIT) ) 11 #define iscntrl(_c) ( _pctype[_c] & _CONTROL )
#define _tolower(_c) ( (_c)-'A'+'a' ) #define _toupper(_c) ( (_c)-'a'+'A' )
4. 转换表可能遇到的问题
这种方法的弊端是,对于某些错误的参数,宏会产生错误的代码。如果一个宏的参数不在它的定义域内,那么执行这个宏时,它就会访问转换表之外的存储空间。
如当测试某些比较生僻的字符代码时,若符号位被置为,那么参数会是一个负数,在函数的定义域之外。
对于EOF符号,也要慎重处理。
书上貌似没有提对于转换表实现方式的遇到问题的解决方案 -_-|||
5. 区域设置
当区域设置改变时,我们的字符分类也可能会发生相应的改变。
6.静态存储空间
库可以使用指向表的指针的可写的静态存储空间,但我们不能在程序中不同控制线程中共享一个相同的数据对象。
上面的实现没有使用静态存储空间。
7.实践的实现中都是使用宏的吗?
不小心看到mingw的实现并不是用的宏,代码如下:
#define __ISCTYPE(c, mask) (MB_CUR_MAX == 1 ? (_pctype[c] & mask) : _isctype(c, mask)) __CRT_INLINE int __cdecl __MINGW_NOTHROW isalnum(int c) {return __ISCTYPE(c, (_ALPHA|_DIGIT));} __CRT_INLINE int __cdecl __MINGW_NOTHROW isalpha(int c) {return __ISCTYPE(c, _ALPHA);} __CRT_INLINE int __cdecl __MINGW_NOTHROW iscntrl(int c) {return __ISCTYPE(c, _CONTROL);} __CRT_INLINE int __cdecl __MINGW_NOTHROW isdigit(int c) {return __ISCTYPE(c, _DIGIT);} __CRT_INLINE int __cdecl __MINGW_NOTHROW isgraph(int c) {return __ISCTYPE(c, (_PUNCT|_ALPHA|_DIGIT));} __CRT_INLINE int __cdecl __MINGW_NOTHROW islower(int c) {return __ISCTYPE(c, _LOWER);} __CRT_INLINE int __cdecl __MINGW_NOTHROW isprint(int c) {return __ISCTYPE(c, (_BLANK|_PUNCT|_ALPHA|_DIGIT));} __CRT_INLINE int __cdecl __MINGW_NOTHROW ispunct(int c) {return __ISCTYPE(c, _PUNCT);} __CRT_INLINE int __cdecl __MINGW_NOTHROW isspace(int c) {return __ISCTYPE(c, _SPACE);} __CRT_INLINE int __cdecl __MINGW_NOTHROW isupper(int c) {return __ISCTYPE(c, _UPPER);} __CRT_INLINE int __cdecl __MINGW_NOTHROW isxdigit(int c) {return __ISCTYPE(c, _HEX);}
使用了内联函数的实现,相对于宏更加安全了,另外把预处理的工作交给了编译器,让编译器在代码量和代码效率间自动进行抉择。
完