当前位置: 首页 > news >正文

C语言中scanf函数的实现

在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/

相关文章:

  • 【codevs 1225】八数码难题
  • [codevs 1288] 埃及分数 [IDdfs 迭代加深搜索 ]
  • 浅谈一类积性函数的前缀和
  • Codeforces Round #363 (Div. 2)[B]One Bomb
  • BFS、双向BFS和A*
  • 二分的模板(花式二分)
  • STL之set集合容器
  • NOIP2016#模拟考试 Day.1# T1 洗澡
  • NOIP2016#模拟考试 Day.1# T3 导航软件
  • [hdu 4405] Aeroplane chess [概率DP 期望]
  • NOIP2016#模拟考试 Day.2# T2 网络修复 【LCA + 并查集】
  • NOIP2016#模拟考试 Day.2# T3 王位继承
  • [hdu 2826] The troubles of lmy [简单计算几何 - 相似]
  • [hdu 2896] 病毒侵袭 [ac自动机][病毒特征码匹配]
  • [hdu 3065] 病毒侵袭持续中 [AC自动机] [病毒特征码匹配]
  • @jsonView过滤属性
  • 03Go 类型总结
  • Angular6错误 Service: No provider for Renderer2
  •  D - 粉碎叛乱F - 其他起义
  • ES6系统学习----从Apollo Client看解构赋值
  • Leetcode 27 Remove Element
  • leetcode98. Validate Binary Search Tree
  • MySQL QA
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • SpringBoot几种定时任务的实现方式
  • Vim 折腾记
  • 测试如何在敏捷团队中工作?
  • 从零开始的无人驾驶 1
  • 第十八天-企业应用架构模式-基本模式
  • 开发基于以太坊智能合约的DApp
  • 老板让我十分钟上手nx-admin
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 浅谈Golang中select的用法
  • 使用权重正则化较少模型过拟合
  • 微服务核心架构梳理
  • NLPIR智能语义技术让大数据挖掘更简单
  • Prometheus VS InfluxDB
  • 我们雇佣了一只大猴子...
  • ​2020 年大前端技术趋势解读
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #Spring-boot高级
  • #每日一题合集#牛客JZ23-JZ33
  • (1)(1.13) SiK无线电高级配置(六)
  • (C#)获取字符编码的类
  • (第一天)包装对象、作用域、创建对象
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (十一)图像的罗伯特梯度锐化
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .gitignore文件—git忽略文件
  • .Net MVC + EF搭建学生管理系统
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • [] 与 [[]], -gt 与 > 的比较
  • []AT 指令 收发短信和GPRS上网 SIM508/548