zoukankan      html  css  js  c++  java
  • C语言中scanf函数的实现

    接上一篇C语言中可变参数函数实现原理,从理论上详细介绍了C语言中可变参数函数的实现,这一篇从minix内核源码中的scanf函数入手,学习C语言经典可变参数函数的实现过程

    在scanf.c文件中,可以看到scanf函数,代码如下:

    #include    <stdio.h>
    #include    <stdarg.h>
    #include    "loc_incl.h"
    
    int scanf(const char *format, ...)
    {
        va_list ap;
        int retval;
    
        va_start(ap, format);
    
        retval = _doscan(stdin, format, ap);
    
        va_end(ap);
    
        return retval;
    }

     对于va_list、va_start、va_end等在stdarg.h头文件中定义的宏,都已经在(stdarg.h头文件源代码分析)一文中介绍过。

    在上述代码中我们可以看到有一个_doscan函数,而这一函数在头文件loc_incl.h中定义,函数声明如下:

    int _doscan(FILE * stream, const char *format, va_list ap);

    _doscan函数的实现源代码如下:

      1 int
      2 _doscan(register FILE *stream, const char *format, va_list ap)
      3 {
      4     int        done = 0;    /* number of items done */
      5     int        nrchars = 0;    /* number of characters read */
      6     int        conv = 0;    /* # of conversions */
      7     int        base;        /* conversion base */
      8     unsigned long    val;        /* an integer value */
      9     register char    *str;        /* temporary pointer */
     10     char        *tmp_string;    /* ditto */
     11     unsigned    width = 0;    /* width of field */
     12     int        flags;        /* some flags */
     13     int        reverse;    /* reverse the checking in [...] */
     14     int        kind;
     15     register int    ic = EOF;    /* the input character */
     16 #ifndef    NOFLOAT
     17     long double    ld_val;
     18 #endif
     19 
     20     if (!*format) return 0;
     21 
     22     while (1) {
     23         if (isspace(*format)) {
     24             while (isspace(*format))
     25                 format++;    /* skip whitespace */
     26             ic = getc(stream);
     27             nrchars++;
     28             while (isspace (ic)) {
     29                 ic = getc(stream);
     30                 nrchars++;
     31             }
     32             if (ic != EOF) ungetc(ic,stream);
     33             nrchars--;
     34         }
     35         if (!*format) break;    /* end of format */
     36 
     37         if (*format != '%') {
     38             ic = getc(stream);
     39             nrchars++;
     40             if (ic != *format++) break;    /* error */
     41             continue;
     42         }
     43         format++;
     44         if (*format == '%') {
     45             ic = getc(stream);
     46             nrchars++;
     47             if (ic == '%') {
     48                 format++;
     49                 continue;
     50             }
     51             else break;
     52         }
     53         flags = 0;
     54         if (*format == '*') {
     55             format++;
     56             flags |= FL_NOASSIGN;
     57         }
     58         if (isdigit (*format)) {
     59             flags |= FL_WIDTHSPEC;
     60             for (width = 0; isdigit (*format);)
     61                 width = width * 10 + *format++ - '0';
     62         }
     63 
     64         switch (*format) {
     65         case 'h': flags |= FL_SHORT; format++; break;
     66         case 'l': flags |= FL_LONG; format++; break;
     67         case 'L': flags |= FL_LONGDOUBLE; format++; break;
     68         }
     69         kind = *format;
     70         if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
     71             do {
     72                 ic = getc(stream);
     73                 nrchars++;
     74             } while (isspace(ic));
     75             if (ic == EOF) break;        /* outer while */
     76         } else if (kind != 'n') {        /* %c or %[ */
     77             ic = getc(stream);
     78             if (ic == EOF) break;        /* outer while */
     79             nrchars++;
     80         }
     81         switch (kind) {
     82         default:
     83             /* not recognized, like %q */
     84             return conv || (ic != EOF) ? done : EOF;
     85             break;
     86         case 'n':
     87             if (!(flags & FL_NOASSIGN)) {    /* silly, though */
     88                 if (flags & FL_SHORT)
     89                     *va_arg(ap, short *) = (short) nrchars;
     90                 else if (flags & FL_LONG)
     91                     *va_arg(ap, long *) = (long) nrchars;
     92                 else
     93                     *va_arg(ap, int *) = (int) nrchars;
     94             }
     95             break;
     96         case 'p':        /* pointer */
     97             set_pointer(flags);
     98             /* fallthrough */
     99         case 'b':        /* binary */
    100         case 'd':        /* decimal */
    101         case 'i':        /* general integer */
    102         case 'o':        /* octal */
    103         case 'u':        /* unsigned */
    104         case 'x':        /* hexadecimal */
    105         case 'X':        /* ditto */
    106             if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
    107                 width = NUMLEN;
    108             if (!width) return done;
    109 
    110             str = o_collect(ic, stream, kind, width, &base);
    111             if (str < inp_buf
    112                 || (str == inp_buf
    113                     && (*str == '-'
    114                     || *str == '+'))) return done;
    115 
    116             /*
    117              * Although the length of the number is str-inp_buf+1
    118              * we don't add the 1 since we counted it already
    119              */
    120             nrchars += str - inp_buf;
    121 
    122             if (!(flags & FL_NOASSIGN)) {
    123                 if (kind == 'd' || kind == 'i')
    124                     val = strtol(inp_buf, &tmp_string, base);
    125                 else
    126                     val = strtoul(inp_buf, &tmp_string, base);
    127                 if (flags & FL_LONG)
    128                     *va_arg(ap, unsigned long *) = (unsigned long) val;
    129                 else if (flags & FL_SHORT)
    130                     *va_arg(ap, unsigned short *) = (unsigned short) val;
    131                 else
    132                     *va_arg(ap, unsigned *) = (unsigned) val;
    133             }
    134             break;
    135         case 'c':
    136             if (!(flags & FL_WIDTHSPEC))
    137                 width = 1;
    138             if (!(flags & FL_NOASSIGN))
    139                 str = va_arg(ap, char *);
    140             if (!width) return done;
    141 
    142             while (width && ic != EOF) {
    143                 if (!(flags & FL_NOASSIGN))
    144                     *str++ = (char) ic;
    145                 if (--width) {
    146                     ic = getc(stream);
    147                     nrchars++;
    148                 }
    149             }
    150 
    151             if (width) {
    152                 if (ic != EOF) ungetc(ic,stream);
    153                 nrchars--;
    154             }
    155             break;
    156         case 's':
    157             if (!(flags & FL_WIDTHSPEC))
    158                 width = 0xffff;
    159             if (!(flags & FL_NOASSIGN))
    160                 str = va_arg(ap, char *);
    161             if (!width) return done;
    162 
    163             while (width && ic != EOF && !isspace(ic)) {
    164                 if (!(flags & FL_NOASSIGN))
    165                     *str++ = (char) ic;
    166                 if (--width) {
    167                     ic = getc(stream);
    168                     nrchars++;
    169                 }
    170             }
    171             /* terminate the string */
    172             if (!(flags & FL_NOASSIGN))
    173                 *str = '\0';    
    174             if (width) {
    175                 if (ic != EOF) ungetc(ic,stream);
    176                 nrchars--;
    177             }
    178             break;
    179         case '[':
    180             if (!(flags & FL_WIDTHSPEC))
    181                 width = 0xffff;
    182             if (!width) return done;
    183 
    184             if ( *++format == '^' ) {
    185                 reverse = 1;
    186                 format++;
    187             } else
    188                 reverse = 0;
    189 
    190             for (str = Xtable; str < &Xtable[NR_CHARS]
    191                             ; str++)
    192                 *str = 0;
    193 
    194             if (*format == ']') Xtable[*format++] = 1;
    195 
    196             while (*format && *format != ']') {
    197                 Xtable[*format++] = 1;
    198                 if (*format == '-') {
    199                     format++;
    200                     if (*format
    201                         && *format != ']'
    202                         && *(format) >= *(format -2)) {
    203                         int c;
    204 
    205                         for( c = *(format -2) + 1
    206                             ; c <= *format ; c++)
    207                             Xtable[c] = 1;
    208                         format++;
    209                     }
    210                     else Xtable['-'] = 1;
    211                 }
    212             }
    213             if (!*format) return done;
    214             
    215             if (!(Xtable[ic] ^ reverse)) {
    216             /* MAT 8/9/96 no match must return character */
    217                 ungetc(ic, stream);
    218                 return done;
    219             }
    220 
    221             if (!(flags & FL_NOASSIGN))
    222                 str = va_arg(ap, char *);
    223 
    224             do {
    225                 if (!(flags & FL_NOASSIGN))
    226                     *str++ = (char) ic;
    227                 if (--width) {
    228                     ic = getc(stream);
    229                     nrchars++;
    230                 }
    231             } while (width && ic != EOF && (Xtable[ic] ^ reverse));
    232 
    233             if (width) {
    234                 if (ic != EOF) ungetc(ic, stream);
    235                 nrchars--;
    236             }
    237             if (!(flags & FL_NOASSIGN)) {    /* terminate string */
    238                 *str = '\0';    
    239             }
    240             break;
    241 #ifndef    NOFLOAT
    242         case 'e':
    243         case 'E':
    244         case 'f':
    245         case 'g':
    246         case 'G':
    247             if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
    248                 width = NUMLEN;
    249 
    250             if (!width) return done;
    251             str = f_collect(ic, stream, width);
    252 
    253             if (str < inp_buf
    254                 || (str == inp_buf
    255                 && (*str == '-'
    256                     || *str == '+'))) return done;
    257 
    258             /*
    259              * Although the length of the number is str-inp_buf+1
    260              * we don't add the 1 since we counted it already
    261              */
    262             nrchars += str - inp_buf;
    263 
    264             if (!(flags & FL_NOASSIGN)) {
    265                 ld_val = strtod(inp_buf, &tmp_string);
    266                 if (flags & FL_LONGDOUBLE)
    267                     *va_arg(ap, long double *) = (long double) ld_val;
    268                 else
    269                     if (flags & FL_LONG)
    270                     *va_arg(ap, double *) = (double) ld_val;
    271                 else
    272                     *va_arg(ap, float *) = (float) ld_val;
    273             }
    274             break;
    275 #endif
    276         }        /* end switch */
    277         conv++;
    278         if (!(flags & FL_NOASSIGN) && kind != 'n') done++;
    279         format++;
    280     }
    281     return conv || (ic != EOF) ? done : EOF;
    282 }
    _doscan函数代码

     在上面的源代码中,值得注意的是第26行的getc宏,定义代码如下:

    #define    getc(p)        (--(p)->_count >= 0 ? (int) (*(p)->_ptr++) : \
                    __fillbuf(p))

    getc的调用形式:ch=getc(fp); 功能是从文件指针指向的文件读入一个字符,并把它作为函数值返回给int型变量ch。

    第4行~第17行,定义一些后面需要用到的变量

    第23行~34行,跳过format格式串中的空格,并且跳过输入流中的空格

    第37行~42行,输入流stream与format格式串中的空白符(空白符可以是空格(space)、制表符(tab)和新行符(newline))保持一致

    第44行~52行,在format中的字符为'%'的前提下,stream中的字符也为'%',则继续

    第54行~57行,format当前字符为'*',表示读指定类型的数据但不保存

    第58行~62行,指定说明最大域宽。 在百分号(%)与格式码之间的整数用于限制从对应域读入的最大字符数于宽度

    第64行~282行,switch语句,用于格式修饰符,这些修饰符包括: h、l、L、c、p、b、d、i、o、u……,还有基于扫描集的'['修饰符


    对scanf函数的源码分析,需要在scanf函数的语法格式详细的理解基础上进行,由于scanf函数实现十分复杂,需要仔细的品味,这里只是比较初步的分析,具体还有待后期不断的完善

  • 相关阅读:
    计算机网络基础:TCP运输连接管理(三次握手 + 四次挥手)
    Zookeeper集群及配置
    Maven安装及配置
    SpringMVC拦截器+Spring自定义注解实现权限验证
    Spring之AOP配置
    设置Google浏览器不缓存JS
    数据加密之AES
    SpringMVC配置多个自定义拦截器
    利用Zookeeper实现分布式锁
    数据加密之RSA
  • 原文地址:https://www.cnblogs.com/cpoint/p/3373263.html
Copyright © 2011-2022 走看看