接上一篇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函数的实现源代码如下:

 int
_doscan(register FILE *stream, const char *format, va_list ap)
{
int done = ; /* number of items done */
int nrchars = ; /* number of characters read */
int conv = ; /* # of conversions */
int base; /* conversion base */
unsigned long val; /* an integer value */
register char *str; /* temporary pointer */
char *tmp_string; /* ditto */
unsigned width = ; /* 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 ; while () {
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 = ;
if (*format == '*') {
format++;
flags |= FL_NOASSIGN;
}
if (isdigit (*format)) {
flags |= FL_WIDTHSPEC;
for (width = ; isdigit (*format);)
width = width * + *format++ - '';
} 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 = ;
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 = ;
format++;
} else
reverse = ; for (str = Xtable; str < &Xtable[NR_CHARS]
; str++)
*str = ; if (*format == ']') Xtable[*format++] = ; while (*format && *format != ']') {
Xtable[*format++] = ;
if (*format == '-') {
format++;
if (*format
&& *format != ']'
&& *(format) >= *(format -)) {
int c; for( c = *(format -) +
; c <= *format ; c++)
Xtable[c] = ;
format++;
}
else Xtable['-'] = ;
}
}
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宏,定义代码如下:

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

C语言中scanf函数的实现的更多相关文章

  1. (转)C语言中scanf函数与空格回车

    来源:http://blog.csdn.net/xia7139/article/details/14522493

  2. 关于C语言中printf函数“输出歧视”的问题

    目录 关于C语言中printf函数"输出歧视"的问题 问题描述 探索问题原因 另一种研究方法 问题结论 关于C语言中printf函数"输出歧视"的问题 问题描述 ...

  3. c语言中的c语言中realloc()函数解析

    c语言中realloc()函数解析 真是有点惭愧,这些内容本应该很早就掌握的,以前只是糊里糊涂的用,不知道在内存中具体是怎么回事,现在才弄清楚. realloc(void *__ptr, size_t ...

  4. C语言中qsort函数用法

    C语言中qsort函数用法-示例分析    本文实例汇总介绍了C语言中qsort函数用法,包括针对各种数据类型参数的排序,非常具有实用价值非常具有实用价值. 分享给大家供大家参考.C语言中的qsort ...

  5. C语言中system()函数的用法总结(转)

    system()函数功能强大,很多人用却对它的原理知之甚少先看linux版system函数的源码: #include <sys/types.h> #include <sys/wait ...

  6. 使用C语言中qsort()函数对浮点型数组无法成功排序的问题

    一 写在开头 1.1 本节内容 本节主要内容是有关C语言中qsort()函数的探讨. 二 问题和相应解决方法 qsort()是C标准库中的一个通用的排序函数.它既能对整型数据进行排序也能对浮点型数据进 ...

  7. C语言中malloc函数返回值是否需要类型强制转换问题

    1. 在C语言中, 如果调用的函数没有函数原型, 则其返回值将默认为 int 型. 考虑调用malloc函数时忘记了 #include <stdlib.h>的情况 此时malloc函数返回 ...

  8. R语言中apply函数

    前言 刚开始接触R语言时,会听到各种的R语言使用技巧,其中最重要的一条就是不要用循环,效率特别低,要用向量计算代替循环计算. 那么,这是为什么呢?原因在于R的循环操作for和while,都是基于R语言 ...

  9. (转)C语言中Exit函数的使用

    C语言中Exit函数的使用 exit() 结束当前进程/当前程序/,在整个程序中,只要调用 exit ,就结束return() 是当前函数返回,当然如果是在主函数main, 自然也就结束当前进程了,如 ...

随机推荐

  1. .net framework 4.0 在 VS2010 安装目录下位置 dotNetFx40_Full_x86_x64.exe在磁盘哪个目录?

    .net framework 4.0 在 VS2010 安装目录下位置 dotNetFx40_Full_x86_x64.exe在磁盘哪个目录? 使用VS2010开发应用程序完毕后,在发布应用程序时,常 ...

  2. Linq 实现两个对象实例List之间的赋值

    public class UserCopy { public class LoginEntity { public string UserName { get; set; } public strin ...

  3. mac osx voice over的使用

    mac下的filezilla是一个很奇葩的应用,奇葩在哪?有一次我不知道怎么操作的,把filezilla搬到到了窗体顶部,结果被mac的菜单栏挡住了标题栏,然后再也无法移动窗体了,以下是我为了移动它做 ...

  4. koa使用koa-passport实现路由进入前登录验证

    现在的项目需求很简单,当进入一个页面的时候,如果没登录,则跳转到登录页面,如果登录了则直接到对应页面. koa2写的项目,使用koa-passport,koa-session,根据koa-passpo ...

  5. Webkit内核探究【1】——Webkit简介

    出处:http://www.cnblogs.com/jyli/archive/2010/01/31/1660355.html作者:李嘉昱 研究Webkit内核已经有一段时间了,在这期间我花了很多时间去 ...

  6. Bootstrap 各种进度条详解

    一:默认的进度条 创建一个基本的进度条的步骤如下: 添加一个带有 class .progress 的 <div>. 接着,在上面的 <div> 内,添加一个带有 class . ...

  7. Java多线程-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier

    Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-从一个错误的双重校验锁 ...

  8. PHP 5.4 中的traits

    PHP 5.4中的traits,是新引入的特性,中文还真不知道如何准确翻译好.其实际的目的,是为了有的场合想用多继承,但PHP又没多继承,于是就发明了这样的一个东西. Traits (横向重用/多重继 ...

  9. mysql触发器的使用 想让b字段在更新的时候把旧数据保存到a字段中

    使用mysql希望数据库自动触发一些规则,进行更新数据的时候,就需要用触发器了,比如 将旧数据保存到额外字段中,如何做呢? 在abc表中 name更新的时候 我希望把name的老数据保存到 old_n ...

  10. ios开发之--调整UISearchBar的输入框的背景颜色

    遍历UISearchBar的子视图,找到输入框坐在的view,添加背景颜色即可. 代码如下: UISearchBar *searchBar = [[UISearchBar alloc] initWit ...