在没读<ctype.h>的源码之前,我一直以为我们平时用的isalnum、isdigit、isalpha等这些函数,是靠判断写出来的。

比如:

int isdigit(int c){
return ( ((char)c >= '0' && (char)c <='9') ? 1 : 0 );
}

在没有阅读源码之前,可能大多数人都会这么做,其实这样做是正确的。但是我在看了源码之后,才发现标准库并不是这样来实现这些函数的。是靠转换表来高效的实现的。

这是<ctype.h>的字符类别(第一次用Xmind6这玩意...)
内容:
isalnum(int c): 判别所有isalpha或者isdigit判别为真的字符。
isalpha(int c): 判别所有isupper或者islower判别为真的字符,或者那些实现定义的字符集中的iscntrl、isdigit、ispunct、和isspace判别都不为真的字符。
iscntrl(int c); 判别所有的控制字符。
isdigit(int c); 判别所有的十进制数字字符。
isgraph(int c); 判别除空格(' ')之外的所有打印字符。
islower(int c); 判别所有的小写字母。
isprint(int c); 判别包括空格(' ')在内的所有打印字符。
ispunct(int c); 判别除空格(' ')和isalnum判别为真的字符之外的所有打印字符。
isspace(int c); 判别所有标准的空白字符,或者由实现定义的字符集中isalnum判别为假的字符。标准空白字符有:空格(' ')、换页('\f')、换行('\n')、回车('\r')、水平制表符('\t')和垂直制表符('\v')。
isupper(int c); 判别所有的大写字母。
isxdigit(int c); 判别所有的十六进制数字字符。
tolower(int c); 函数tolower把一个大写字母转换为小写字母。
toupper(int c); 函数toupper把一个小写字母转换为大写字母。
实现:大部分好的实现都把EOF值定义为-1,所以转换表中元素的数目一定比字符类型所能表示的所有字符的数目多1。所以一个转换表中一定至少包含257个元素。<ctype.h>
#ifndef _CTYPE
#define _CTYPE /* _Ctype code bits */
#define _XA 0X200/* extra alphabetic */
#define _XS 0x100 /* extra space */
#define _BB 0x80 /* BEL, BS, etc. */
#define _CN 0x40 /* CR, FF, HT, NL, VT */
#define _DI 0x20 /* '0' - '9' */
#define _LO 0x10 /* 'a' - 'z' */
#define _PU 0x08 /* punctuation */
#define _SP 0x04 /* space */
#define _UP 0x02 /* 'A' - 'Z' */
#define _XD 0x01 /* '0' - '9', 'A' - 'F', 'a' - 'f' */ /* declarations */ int isalnum(int), int alpha(int),int iscntrl(int), int isdigit(int);
int isgraph(int), int islower(int),int isprint(int), int ispunct(int);
int isspace(int), int isupper(int),int isxdigit(int);
int tolower(int), inttoupper(int); extern const short *_Ctype, *_Tolower, *_Toupper; /* macro */ #define isalnum(c)(_Ctype[(int)c] & (_DI | _LO | _UP | _XA))
#define isalpha(c)(_Ctype[(int)c] & (_LO | _UP | _XA))
#define iscntrl(c)(_Ctype[(int)c] & (_BB | _CN))
#define isdigit(c)(_Ctype[(int)c] & (_DI))
#define isgraph(c)(_Ctype[(int)c] & (_DI | _LO | _PU | _UP | _XA))
#define islower(c)(_Ctype[(int)c] & (_LO))
#define isprint(c)(_Ctype[(int)c] & (_DI | _LO | _PU | _SP | _UP | _XA))
#define ispunct(c)(_Ctype[(int)c] & (_PU))
#define isspace(c)(_Ctype[(int)c] & (_CN | _SP | _XS))
#define isupper(c)(_Ctype[(int)c] & (_UP))
#define isxdigit(c)(_Ctype[(int)c] & (_XD))
#define tolower(c)_Tolower[(int)c]
#define toupper(c)_Toupper[(int)c] #endif /* _CTYPE */
还有这些函数的文件就不写了,举个例子,其他的都一样:int isalnum (int c)
{
return (_Ctype[(int)c] & (_DI | _LO | _UP | _XA));
}

xctyoe.c 文件:

在这个转换表里的前面,我们还看到了三个宏定义:

#define  XDI  (_DI  | _XD)
#define XLO (_LO | _XD)
#define XUP (_UP | _XD)

加上这个三个宏定义后,我们在判断十六进制和十进制的'0' - '9'时,和判断十六进制的'A' - 'F'或'a' - 'f'和英文字母时,都可以得到正确的结果。

另外,xctype.c的最下面有个const指针,指向了转换表的第二个元素,因此转换表的第一个元素存储的就是EOF了。

下面来看看tolower和toupper函数,它们也各有一张转换表:

xtolower.c :

xupper.c :

在xtolower.c中,我们看到从ASCII码值为65开始,是小写字母,从97开始,依然是小写字母,所以这张表,当tolower函数传进去的是大写字母时,被转换为对应的小写字母,如果传进去的是小写字母,则不变。同理,xtoupper.c也是这样。至于表中剩余其它的值,我们不用管。

我们可能会想,自己写像如开头那样的函数实现也可以啊,转化表一做感觉代码挺长的,其实,<ctype.h>里做成转换表而不是直接实现每个函数,是因为数组的访问速度非常快,效率高。而且三张表就可以实现所有的函数的实现!

《C标准库》——之<ctype.h>的更多相关文章

  1. 彻底弄清c标准库中string.h里的常用函数用法

    在我们平常写的c/c++程序,一些算法题中,我们常常会用到c标准库中string.h文件中的函数,这些函数主要用于处理内存,字符串相关操作,是很有用的工具函数.而且有些时候,在笔试或面试中也会出现让你 ...

  2. 走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数

    我的strcat: char *strcat(char *dest,char *src) { char * reval = dest; while(*dest) dest++; while(*src) ...

  3. 走进C标准库(3)——"stdio.h"中的getc和ungetc

    接前文. 再来看看getc和ungetc的实现.在看这两个函数的实现之前,我们先来想一想这两个函数分别需要做的工作. int getc(FILE *stream) 说明:函数getc从stream指向 ...

  4. 走进C标准库(2)——"stdio.h"中的fopen函数

    其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...

  5. 走进C标准库(1)——assert.h,ctype.h

    默默觉得原来的阅读笔记的名字太土了,改了个名字,叫做走进C标准库. 自己就是菜鸟一只,第一次具体看C标准库,文章参杂了对<the standard C library>的阅读和对源码的一些 ...

  6. C 非标准库(conio.h)

    所谓的 C 标准库(C standard library),是指在 ISO C 或者 POSIX 标准中定义的: POSIX is a superset(超集) of the standard C l ...

  7. 走进C标准库(4)——"stdio.h"中的putc

    花了点时间把园子弄得好看了点,现在继续. 函数名: putc 功  能: 输出一字符到指定流中 用  法: int putc(int ch, FILE *stream); #define _putc_ ...

  8. 走进C标准库(5)——"stdio.h"中的其他部分函数

    函数介绍来自:http://ganquan.info/standard-c/ 函数名: freopen 功  能: 替换一个流 用  法: FILE *freopen(char *filename, ...

  9. 走进C标准库(6)——"string.h"中函数的实现memchr

    我写的memchr: void *memchr(const void *buf, char ch, unsigned count){ unsigned ; while(*(buf++) != ch & ...

  10. 走进C标准库(7)——"string.h"中函数的实现memcmp,memcpy,memmove,memset

    我的memcmp: int memcmp(void *buf1, void *buf2, unsigned int count){ int reval; while(count && ...

随机推荐

  1. Elasticsearch内存分配设置详解

    Elasticsearch默认安装后设置的内存是1GB,对于任何一个现实业务来说,这个设置都太小了.如果你正在使用这个默认堆内存配置,你的集群配置可能会很快发生问题. 这里有两种方式修改Elastic ...

  2. 最长公共上升子序列(LICS) 模板

    void LICS() { ;i<=n;i++) { ; ;j<=n;j++) { if (a[i]==b[j]) f[i][j]=ma+; ][j]; ][j]>ma) ma=f[ ...

  3. 【个人使用.Net类库】(1)INI配置文件操作类

    开发接口程序时,对于接口程序配置的IP地址.端口等都需要是可配置的,而在Win Api原生实现了INI文件的读写操作,因此只需要调用Win Api中的方法即可操作INI配置文件,关键代码就是如何调用W ...

  4. POJ 2763

    题意:给一个数,边之间有权值,然后两种操作,第一种:求任意两点的权值和,第二,修改树上两点的权值. #pragma comment(linker, "/STACK:1024000000,10 ...

  5. key 4v4

    #include "key4v4.h" #include "stm32f10x.h" #include "delay.h" /* PA4-L ...

  6. java基础-007

    41.Servlet Servlet 是处理客户端请求并产生动态网页内容的Java类.Servlet主要是用来处理或者存储HTML表单提交的数据,产生动态内容,在无状态的HTTP协议下管理状态信息.所 ...

  7. Windows下LDAP服务器配置

    LDAP即轻量级目录访问协议(Lightweight Directory Access Protocol),基础知识不再赘述,本文主要记录我的配置与安装过程. LDAP for windows下载 o ...

  8. Oracle 12c与GoldenGate 12c的一些问答

    1. 如何知道一个12c DB是否为容器数据库?(1) container DBSQL> select cdb from v$database;CDB---YES (2) non contain ...

  9. UIViewController添加子控制器(addChildViewController)

    // //  TaskHallViewController.m //  yybjproject // //  Created by bingjun on 15/10/27. //  Copyright ...

  10. Be a person

    做人不能太实诚 尤其是干我们这行的 多久时间能做完 你自己心里要有个估算 然后把时间再往后延 别他妈给自己找罪受