面试题strtoi实现(一)—— 函数的简单实现

来源:互联网 发布:日本人社交软件 编辑:程序博客网 时间:2024/06/04 23:26
大师级程序地址: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/strtol.c

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. static inlineint 
  2. isupper(char c) 
  3.     return (c >= 'A' && c <='Z'); 
  4.  
  5. static inlineint 
  6. isalpha(char c) 
  7.     return ((c >= 'A' && c <='Z') || (c >= 'a' && c <='z')); 
  8.  
  9.  
  10. static inlineint 
  11. isspace(char c) 
  12.     return (c == ' ' || c == '\t' || c =='\n' || c == '\12'); 
  13.  
  14. static inlineint 
  15. isdigit(char c) 
  16.     return (c >= '0' && c <= '9'); 
  17.  
  18. /*
  19. * Convert a string to a long integer.
  20. *
  21. * Ignores `locale' stuff.  Assumes that the upper and lower case
  22. * alphabets and digits are each contiguous.
  23. */ 
  24. long 
  25. strtol(nptr, endptr, base) 
  26.     const char *nptr; 
  27.     char **endptr; 
  28.     register int base; 
  29.     register constchar *s = nptr; 
  30.     register unsigned long acc; 
  31.     register int c; 
  32.     register unsigned long cutoff; 
  33.     register int neg = 0, any, cutlim; 
  34.  
  35.     /*
  36.      * Skip white space and pick up leading +/- sign if any.
  37.      * If base is 0, allow 0x for hex and 0 for octal, else
  38.      * assume decimal; if base is already 16, allow 0x.
  39.      */ 
  40.     do
  41.         c = *s++; 
  42.     } while (isspace(c)); 
  43.     if (c == '-') { 
  44.         neg = 1; 
  45.         c = *s++; 
  46.     } else if (c =='+'
  47.         c = *s++; 
  48.     if ((base == 0 || base == 16) && 
  49.         c == '0' && (*s == 'x' || *s =='X')) { 
  50.         c = s[1]; 
  51.         s += 2; 
  52.         base = 16; 
  53.     } else if ((base == 0 || base == 2) && 
  54.         c == '0' && (*s =='b' || *s == 'B')) { 
  55.         c = s[1]; 
  56.         s += 2; 
  57.         base = 2; 
  58.     } 
  59.     if (base == 0) 
  60.         base = c == '0' ? 8 : 10; 
  61.  
  62.     /*
  63.      * Compute the cutoff value between legal numbers and illegal
  64.      * numbers.  That is the largest legal value, divided by the
  65.      * base.  An input number that is greater than this value, if
  66.      * followed by a legal input character, is too big.  One that
  67.      * is equal to this value may be valid or not; the limit
  68.      * between valid and invalid numbers is then based on the last
  69.      * digit.  For instance, if the range for longs is
  70.      * [-2147483648..2147483647] and the input base is 10,
  71.      * cutoff will be set to 214748364 and cutlim to either
  72.      * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
  73.      * a value > 214748364, or equal but the next digit is > 7 (or 8),
  74.      * the number is too big, and we will return a range error.
  75.      *
  76.      * Set any if any `digits' consumed; make it negative to indicate
  77.      * overflow.
  78.      */ 
  79.     cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; 
  80.     cutlim = cutoff % (unsigned long)base; 
  81.     cutoff /= (unsigned long)base; 
  82.     for (acc = 0, any = 0;; c = *s++) { 
  83.         if (isdigit(c)) 
  84.             c -= '0'
  85.         else if (isalpha(c)) 
  86.             c -= isupper(c) ? 'A' - 10 :'a' - 10; 
  87.         else 
  88.             break
  89.         if (c >= base) 
  90.             break
  91.         if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) 
  92.             any = -1; 
  93.         else
  94.             any = 1; 
  95.             acc *= base; 
  96.             acc += c; 
  97.         } 
  98.     } 
  99.     if (any < 0) { 
  100.         acc = neg ? LONG_MIN : LONG_MAX; 
  101. //      errno = ERANGE; 
  102.     } else if (neg) 
  103.         acc = -acc; 
  104.     if (endptr != 0) 
  105.         *endptr = (char *)(any ? s - 1 : nptr); 
  106.     return (acc); 
  107.  
  108. /*
  109. * Convert a string to an unsigned long integer.
  110. *
  111. * Ignores `locale' stuff.  Assumes that the upper and lower case
  112. * alphabets and digits are each contiguous.
  113. */ 
  114. unsigned long 
  115. strtoul(nptr, endptr, base) 
  116.     const char *nptr; 
  117.     char **endptr; 
  118.     register int base; 
  119.     register constchar *s = nptr; 
  120.     register unsigned long acc; 
  121.     register int c; 
  122.     register unsigned long cutoff; 
  123.     register int neg = 0, any, cutlim; 
  124.  
  125.     /*
  126.      * See strtol for comments as to the logic used.
  127.      */ 
  128.     do
  129.         c = *s++; 
  130.     } while (isspace(c)); 
  131.     if (c == '-') { 
  132.         neg = 1; 
  133.         c = *s++; 
  134.     } else if (c =='+'
  135.         c = *s++; 
  136.     if ((base == 0 || base == 16) && 
  137.         c == '0' && (*s == 'x' || *s =='X')) { 
  138.         c = s[1]; 
  139.         s += 2; 
  140.         base = 16; 
  141.     } else if ((base == 0 || base == 2) && 
  142.         c == '0' && (*s =='b' || *s == 'B')) { 
  143.         c = s[1]; 
  144.         s += 2; 
  145.         base = 2; 
  146.     } 
  147.     if (base == 0) 
  148.         base = c == '0' ? 8 : 10; 
  149.     cutoff = (unsigned long)ULONG_MAX / (unsignedlong)base; 
  150.     cutlim = (unsigned long)ULONG_MAX % (unsignedlong)base; 
  151.     for (acc = 0, any = 0;; c = *s++) { 
  152.         if (isdigit(c)) 
  153.             c -= '0'
  154.         else if (isalpha(c)) 
  155.             c -= isupper(c) ? 'A' - 10 :'a' - 10; 
  156.         else 
  157.             break
  158.         if (c >= base) 
  159.             break
  160.         if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) 
  161.             any = -1; 
  162.         else
  163.             any = 1; 
  164.             acc *= base; 
  165.             acc += c; 
  166.         } 
  167.     } 
  168.     if (any < 0) { 
  169.         acc = ULONG_MAX; 
  170. //      errno = ERANGE; 
  171.     } else if (neg) 
  172.         acc = -acc; 
  173.     if (endptr != 0) 
  174.         *endptr = (char *)(any ? s - 1 : nptr); 
  175.     return (acc); 

     我们来分析下strtol程序逻辑吧。程序里看到了几个inline函数,这个是实现ctype.h里面字符类型判断

的函数,为后面扫描字符时做准备。使用inline函数的好处是高效,适用于函数代码短小的情况。

register变量存放在寄存器中,CPU对其访问比内存中变量的访问快,适合需要频繁访问的变量。

里面除了定义了用于扫描字串的指针s(指向当前扫描字符的下一个),还定义指向当前扫描字符的c。

(1)程序能处理base为0或者16,而处理前导空格符及正负号后的字串以0x,0X开头的情况(16进制)。

(2)程序能处理base为0或者2,而处理前导空格符及正负号后的字串以0b,0B开头的情况(2进制)。

(3)似乎程序并没有处理base为8,而处理前导空格符及正负号后的字串以'0'开头的情况(8进制)。

事实上,任何进制数的前导0,是”隐式“被处理了的。因为累加和的计算方式是当前的和加上下一个被计入

的字符。如果发现了前导0,扫描后,当前累加和还是0,sum * base + [ 下一个字符数值] =[ 下一个字符数值]

(4)这里有个疑问:这部分程序的目的是想让c被赋值为”处理前导空格,制表符及符号符之后的第一个字符“,

而s指向c后面的字符。如果输入是"0x”或者“0X”怎么办?我们知道,c为'\0',那s呢? 此处是否涉及到越界?

(5)看见那个cutoff没?那个可是个unsigned int类型的,所以能够”容纳“INT_MAX或者-INT_MIN。与我

上一篇文章程序中overflow的意图是一样的。cutlim为cutoff除以base所得余数;之后cutoff变为所能达到

最大值的绝对值除以base所得的”商“。看到这个商和余数,联想到了什么?

如果已经扫描到的数已经大于这个“最大商”,那么,接下来就不能出现合法字符(数字,字母),否则溢出。

因为:sum * base +[ 下一个字符数值] > sum * base > 当前符号下所对应的最大数值

如果已经扫描到的数等于这个“最大商”,那么接下来扫描到的合法字符(数字,字母)不能超过此时的“最大余数”。

sum * base +[ 下一个字符数值] < sum * base +  "最大余数“ = 当前符号可表示的最大数的绝对值

如果已经扫描到的数小于这个“最大商”,那么接下来扫描到的合法字符(数字,字母)不能超过此时的“最大余数”。

sum * base +[ 下一个字符数值] < ”最大商“ * base +  "最大余数“ = 当前符号可表示的最大数的绝对值

由此,我们得出了扫描数溢出的条件:已经扫描到的数值(sum) > ”最大商“ 或者 (sum == "最大商” 并且

当前扫描的字符数值 > "最大余数“ 。

(6)看看循环里的条件c = *s++,感觉存在上述疑问(4)中提出的问题:c为结束符'\0'时,s指向哪里?

另外,判断溢出时,条件any < 0 是必要的么?感觉根本不需要。

(7)退出循环后,any < 0 对应着数值溢出的情况。对endptr是否为NULL进行判断,如果未NULL,说明

未指向有效的内存,当然不能引用。如果any不为0 (上面看到是1),说明已经扫描到了有效字符,s -1 指

c,对于扫描完所有字符的情况,此时c为'\0' ;对于包含非法字符的情况,则c为找到的第一个非法字符。

(8)错误代码时存放在全局变量errno,与我前一篇定义错误码并作为strtoi函数返回值是不一样的。

    

    大家也可参考另一类似程序:http://blog.csdn.net/ammana_babi/article/details/1473336

    

    在网上闲逛,无意中看到了JULY大神写的一篇关于strtoInt的文章。该文从题目分析开始,先拿出大家

容易写出的”错误程序”。然后深入分析,找出该如何去进行错误检查和处理,如何可以优化程序。除了

点赞,我不知道还能做些啥,分享给大家,希望大家有空一定要去看看。网址:JULY的strtoInt

      

     到此,文章要结束了。大家还记得兄弟篇的第一篇么? 在此也附上网址。

面试题strtoi实现(一)—— 函数的简单实现

0 0
原创粉丝点击