C语言中scanf函数的实现

来源:互联网 发布:java比较日期相差一年 编辑:程序博客网 时间:2024/05/20 09:08

在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函数的实现源代码如下:

int_doscan(register FILE *stream, const char *format, va_list ap){    int        done = 0;    /* number of items done */    int        nrchars = 0;    /* number of characters read */    int        conv = 0;    /* # of conversions */    int        base;        /* conversion base */    unsigned long    val;        /* an integer value */    register char    *str;        /* temporary pointer */    char        *tmp_string;    /* ditto */    unsigned    width = 0;    /* width of field */    int        flags;        /* some flags */    int        reverse;    /* reverse the checking in [...] */    int        kind;    register int    ic = EOF;    /* the input character */#ifndef    NOFLOAT    long double    ld_val;#endif    if (!*format) return 0;    while (1) {        if (isspace(*format)) {            while (isspace(*format))                format++;    /* skip whitespace */            ic = getc(stream);            nrchars++;            while (isspace (ic)) {                ic = getc(stream);                nrchars++;            }            if (ic != EOF) ungetc(ic,stream);            nrchars--;        }        if (!*format) break;    /* end of format */        if (*format != '%') {            ic = getc(stream);            nrchars++;            if (ic != *format++) break;    /* error */            continue;        }        format++;        if (*format == '%') {            ic = getc(stream);            nrchars++;            if (ic == '%') {                format++;                continue;            }            else break;        }        flags = 0;        if (*format == '*') {            format++;            flags |= FL_NOASSIGN;        }        if (isdigit (*format)) {            flags |= FL_WIDTHSPEC;            for (width = 0; isdigit (*format);)                width = width * 10 + *format++ - '0';        }        switch (*format) {        case 'h': flags |= FL_SHORT; format++; break;        case 'l': flags |= FL_LONG; format++; break;        case 'L': flags |= FL_LONGDOUBLE; format++; break;        }        kind = *format;        if ((kind != 'c') && (kind != '[') && (kind != 'n')) {            do {                ic = getc(stream);                nrchars++;            } while (isspace(ic));            if (ic == EOF) break;        /* outer while */        } else if (kind != 'n') {        /* %c or %[ */            ic = getc(stream);            if (ic == EOF) break;        /* outer while */            nrchars++;        }        switch (kind) {        default:            /* not recognized, like %q */            return conv || (ic != EOF) ? done : EOF;            break;        case 'n':            if (!(flags & FL_NOASSIGN)) {    /* silly, though */                if (flags & FL_SHORT)                    *va_arg(ap, short *) = (short) nrchars;                else if (flags & FL_LONG)                    *va_arg(ap, long *) = (long) nrchars;                else                    *va_arg(ap, int *) = (int) nrchars;            }            break;        case 'p':        /* pointer */            set_pointer(flags);            /* fallthrough */        case 'b':        /* binary */        case 'd':        /* decimal */        case 'i':        /* general integer */        case 'o':        /* octal */        case 'u':        /* unsigned */        case 'x':        /* hexadecimal */        case 'X':        /* ditto */            if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)                width = NUMLEN;            if (!width) return done;            str = o_collect(ic, stream, kind, width, &base);            if (str < inp_buf                || (str == inp_buf                    && (*str == '-'                    || *str == '+'))) return done;            /*             * Although the length of the number is str-inp_buf+1             * we don't add the 1 since we counted it already             */            nrchars += str - inp_buf;            if (!(flags & FL_NOASSIGN)) {                if (kind == 'd' || kind == 'i')                    val = strtol(inp_buf, &tmp_string, base);                else                    val = strtoul(inp_buf, &tmp_string, base);                if (flags & FL_LONG)                    *va_arg(ap, unsigned long *) = (unsigned long) val;                else if (flags & FL_SHORT)                    *va_arg(ap, unsigned short *) = (unsigned short) val;                else                    *va_arg(ap, unsigned *) = (unsigned) val;            }            break;        case 'c':            if (!(flags & FL_WIDTHSPEC))                width = 1;            if (!(flags & FL_NOASSIGN))                str = va_arg(ap, char *);            if (!width) return done;            while (width && ic != EOF) {                if (!(flags & FL_NOASSIGN))                    *str++ = (char) ic;                if (--width) {                    ic = getc(stream);                    nrchars++;                }            }            if (width) {                if (ic != EOF) ungetc(ic,stream);                nrchars--;            }            break;        case 's':            if (!(flags & FL_WIDTHSPEC))                width = 0xffff;            if (!(flags & FL_NOASSIGN))                str = va_arg(ap, char *);            if (!width) return done;            while (width && ic != EOF && !isspace(ic)) {                if (!(flags & FL_NOASSIGN))                    *str++ = (char) ic;                if (--width) {                    ic = getc(stream);                    nrchars++;                }            }            /* terminate the string */            if (!(flags & FL_NOASSIGN))                *str = '\0';                if (width) {                if (ic != EOF) ungetc(ic,stream);                nrchars--;            }            break;        case '[':            if (!(flags & FL_WIDTHSPEC))                width = 0xffff;            if (!width) return done;            if ( *++format == '^' ) {                reverse = 1;                format++;            } else                reverse = 0;            for (str = Xtable; str < &Xtable[NR_CHARS]                            ; str++)                *str = 0;            if (*format == ']') Xtable[*format++] = 1;            while (*format && *format != ']') {                Xtable[*format++] = 1;                if (*format == '-') {                    format++;                    if (*format                        && *format != ']'                        && *(format) >= *(format -2)) {                        int c;                        for( c = *(format -2) + 1                            ; c <= *format ; c++)                            Xtable[c] = 1;                        format++;                    }                    else Xtable['-'] = 1;                }            }            if (!*format) return done;                        if (!(Xtable[ic] ^ reverse)) {            /* MAT 8/9/96 no match must return character */                ungetc(ic, stream);                return done;            }            if (!(flags & FL_NOASSIGN))                str = va_arg(ap, char *);            do {                if (!(flags & FL_NOASSIGN))                    *str++ = (char) ic;                if (--width) {                    ic = getc(stream);                    nrchars++;                }            } while (width && ic != EOF && (Xtable[ic] ^ reverse));            if (width) {                if (ic != EOF) ungetc(ic, stream);                nrchars--;            }            if (!(flags & FL_NOASSIGN)) {    /* terminate string */                *str = '\0';                }            break;#ifndef    NOFLOAT        case 'e':        case 'E':        case 'f':        case 'g':        case 'G':            if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)                width = NUMLEN;            if (!width) return done;            str = f_collect(ic, stream, width);            if (str < inp_buf                || (str == inp_buf                && (*str == '-'                    || *str == '+'))) return done;            /*             * Although the length of the number is str-inp_buf+1             * we don't add the 1 since we counted it already             */            nrchars += str - inp_buf;            if (!(flags & FL_NOASSIGN)) {                ld_val = strtod(inp_buf, &tmp_string);                if (flags & FL_LONGDOUBLE)                    *va_arg(ap, long double *) = (long double) ld_val;                else                    if (flags & FL_LONG)                    *va_arg(ap, double *) = (double) ld_val;                else                    *va_arg(ap, float *) = (float) ld_val;            }            break;#endif        }        /* end switch */        conv++;        if (!(flags & FL_NOASSIGN) && kind != 'n') done++;        format++;    }    return conv || (ic != EOF) ? done : EOF;}_doscan函数代码


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

<pre name="code" class="cpp">#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函数实现十分复杂,需要仔细的品味,这里只是比较初步的分析,具体还有待后期不断的完善

作者:cpoint
出处:http://www.cnblogs.com/cpoint/

1 0
原创粉丝点击