一个有意思的问题,结果还不甚明了

今天遇到了一个有意思的问题~

不知道大家用过isdigit函数没有,我今天在写一个测试程序的时候用到了,对于它的用法感到一些不解~不过最后还是找到了答案(但不知道答案正确与否(:)
时间仓促,有些地方或许有错误,写出来大家交流一下~

我是在这种情况下用到的:我从命令行中,输入IP地址和一个最大传输的数据包数
如下:send -n www.ppk.com 50
特意写了如下解析命令行的程序~
void  CSendPackInRange::ValidateArgs(int argc,char **argv)
{
 int i;
 for(i=1;i<argc;i++)
 {
  if((argv[i][0]=='-')||(argv[i][0]=='/'))
   {
     .......
   }
  else
   {
     if(isdigit(argv[i][0]))
       {
        packetsize = atoi(argv[i]);
        }
     else
       {
        destaddr = argv[i];
       }  
   }  
 }
}

很简单的程序,当时想也没有想直接用了isdigit函数,编译联接一切ok ! 下午新来的同事review一下代码,我发现他半天还在看这个段代码,正好下午也没有事,由于乎,泡了一杯咖啡,走过去亲切的问道:"hi,新来的,有什么疑问吗?"
他道:isdigit函数是不是来判断所输入的是不是数字?当时也没有多想就回答他,是~
后又感到不妥,因为像这样的函数我也是很少用,只是从字面意思来判断应该是,但又怕丢面子,自辩道,你调出原型来看一下吧(:
这一调不要紧,以下摘自msdn
int isdigit(
   int c
);
int iswdigit(
   wint_t c
);
Each of these routines returns nonzero if c is a particular representation of a decimal-digit character.

Parameter
c
Integer to test.
Return Value
isdigit returns a non-zero value if c is a decimal digit (0 – 9). iswdigit returns a non-zero value if c is a wide character corresponding to a decimal-digit character. Each of these routines returns 0 if c does not satisfy the test condition.

The result of the test condition for the isdigit function depends on the LC_CTYPE category setting of the current locale; see setlocale for more information. For iswdigit, the result of the test condition is independent of locale.

When used with a debug CRT library, isdigit will display a CRT assert if passed a parameter that isn't EOF or in the range of 0 through 0xFF. When used with a debug CRT library, isdigit will use the parameter as an index into an array, with undefined results if the parameter isn't EOF or in the range of 0 through 0xFF.

 

发现他的参数是整型的,新同事又问道,怎么是整型啊,按你的做法应该是字符型才对啊~看到它像是发现新大陆似的嚷道,心里免不了有些不乐,不过,也是啊~怎么会事?后经一想,有什么大不了的,整型和字符型先天的关系让他们可以互相使用,由于回答道:你仔细看看MSDN关于这个函数的用法,整型和字符型本来就有许多先天的联系!回到坐位上后,由于此类函数只是用,自来没有深究过,借此自己也学习一下,
发现这个函数是这个意思:isdigit(int c)是把c转换成ASCII码来判断是不是数字,在ASCII码里,只有48-57代表"0"-"9"这10个数字,所以只有在输入48-57的时候函数为非0,而其他的数,都返回0。如果你传入的字符,本身就是一个0-9之间的字符,不用说肯定输出非0了.
后一想,那么这个非0到底是多少呢?还是写一测试程序来实验一下吧{:
#include <stdio.h>
#include <ctype.h>
int main()
{
 int temp;
 for(int i = 0;i<10;i++)
 if(temp =isdigit(48+i))
  printf("yes   temp = %d/n",temp);
 else
  printf("no   temp = %d/n",temp);

}
结果有些意想不到,全部输出4!!!
为什么全是4呢~我这个新同事又来经继续发问~(我怎么知道,又不是我写的这个函数,呵呵~心里道)
没有源码也没有办法呢~

后来又把程序放到SOC 下跑了一下,发现输出依然是4,什么原因呢?
然后又在linux下跑了一下,发现输出输出2048 了~
奇怪~
开源,开源,还是开源~有了开源还有什么呢,源码面前了无秘密~答案来自于linux-2.4.0的源代码,
下面代码摘自inux-2.4.0的代码,位于/linux/ctype.h
#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[];
s
#define __ismask(x) (_ctype[(int)(unsigned char)(x)])

#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0)
#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0)
#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
#define isdigit(c) ((__ismask(c)&(_D)) != 0)
#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0)
#define islower(c) ((__ismask(c)&(_L)) != 0)
#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
#define ispunct(c) ((__ismask(c)&(_P)) != 0)
#define isspace(c) ((__ismask(c)&(_S)) != 0)
#define isupper(c) ((__ismask(c)&(_U)) != 0)
#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)

#define isascii(c) (((unsigned char)(c))<=0x7f)
#define toascii(c) (((unsigned char)(c))&0x7f)

static inline unsigned char __tolower(unsigned char c)
{
 if (isupper(c))
  c -= 'A'-'a';
 return c;
}

static inline unsigned char __toupper(unsigned char c)
{
 if (islower(c))
  c -= 'a'-'A';
 return c;
}

#define tolower(c) __tolower(c)
#define toupper(c) __toupper(c)

是不是有些吃惊~我们慢慢来(:
这个头文件里只是定义了一些常量与宏,我们把我们分析的这个宏提取出来:

#define isdigit(c) ((__ismask(c)&(_D)) != 0)

我们按照宏 #define __ismask(x) (_ctype[(int)(unsigned char)(x)])展开:
 
#define isdigit(c)  ((_ctype[(int)(unsigned char )(c)])&(0x04))!=0)

现在把_ctype此符号找出来,替换上看看是什么,符号定义位于linux/libctype.c文件中,如下:

/*
 *  linux/lib/ctype.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */
#include <linux/ctype.h>

unsigned char _ctype[] = {
_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 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,   /* 160-175 */
_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,       /* 176-191 */
_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,       /* 192-207 */
_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L,       /* 208-223 */
_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,       /* 224-239 */
_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L};      /* 240-255 */

 
 
我们再次把宏
#define isdigit(c)  ((_ctype[(int)(unsigned char )(c)])&(0x04))!=0)展开,
以51为例:
#define isdigit(51) ((_ctype[(int)(unsigned char )(51)])&(0x04))!=0)
 
找到51对应的数字:

#define isdigit(51) ((_ctype[_D])&(0X04))!=0)
即为 0x04 & 0x04 结果一看就知道了~
输出为4的原因现在应该知道了吧(:
但是为什么linux下输出2048呢~
朋友们自己分析一下,我也分析一下,现在下班了,贴出来大家看看~
 
一点纠正,isdigit只是一个宏,而非函数,所以前面我们的叫法有所不妥,大家自己改一下即可~
 
总结语:
 开源的ANSI C ,看了其源代码,可以使我们更加准确的理解及本质,可以使我们更好的把握它使用它,这样我们就不会错用或误用!

 

 


 

你可能感兴趣的:(一个有意思的问题,结果还不甚明了)