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

你可能感兴趣的:(scanf)