黑马程序员技术交流社区

标题: 求Scanf 这个为什么输入0.5会无限循环! [打印本页]

作者: senheima    时间: 2015-4-8 23:10
标题: 求Scanf 这个为什么输入0.5会无限循环!
#include <stdio.h>

int main()
{
    int n = 0;
    while (n<=0) {
        printf("请输入一个数字:");
        scanf("%d",&n);
    }
    printf("输入了%d\n",n);
    return 0;
}


作者: 明非    时间: 2015-4-8 23:18
因为你这个逻辑是错误的 while 《=0 你输入0.5 肯定是死循环啊
作者: 波阿波    时间: 2015-4-9 09:09
给楼主个赞!
作者: 无声风铃    时间: 2015-4-9 09:19
因为你输入0.5,而读取时是按照格式化%d读取,第一次读取的为0,再次循环时,由于0已读取还剩".5",而.读取不了所以一直为0。可以加断点调试查看n的值。
作者: 距离    时间: 2015-4-9 09:24
0.5  输入后,n的实际值是0,0<=0  表达式为真。自然就一直循环咯
作者: xiniuniu    时间: 2015-4-9 09:45
这个真不好解释,除非你知道scanf函数是怎样实现的。
作者: xiniuniu    时间: 2015-4-9 09:53
从minix内核源码中的scanf函数入手,学习C语言经典可变参数函数的实现过程

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

  1. #include    <stdio.h>
  2. #include    <stdarg.h>
  3. #include    "loc_incl.h"

  4. int scanf(const char *format, ...)
  5. {
  6.     va_list ap;
  7.     int retval;
  8.     va_start(ap, format);
  9.     retval = _doscan(stdin, format, ap);
  10.     va_end(ap);
  11.     return retval;
  12. }
复制代码

对于va_list、va_start、va_end等在stdarg.h头文件中定义的宏,都已经在(stdarg.h头文件源代码分析)一文中介绍过。在上述代码中我们可以看到有一个_doscan函数,而这一函数在头文件loc_incl.h中定义,函数声明如下:
  1. int _doscan(FILE * stream, const char *format, va_list ap);
复制代码




作者: xiniuniu    时间: 2015-4-9 09:55
_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. if (!*format) return 0;

  20. while (1) {
  21. if (isspace(*format)) {
  22. while (isspace(*format))
  23. format++; /* skip whitespace */
  24. ic = getc(stream);
  25. nrchars++;
  26. while (isspace (ic)) {
  27. ic = getc(stream);
  28. nrchars++;
  29. }
  30. if (ic != EOF) ungetc(ic,stream);
  31. nrchars--;
  32. }
  33. if (!*format) break; /* end of format */

  34. if (*format != '%') {
  35. ic = getc(stream);
  36. nrchars++;
  37. if (ic != *format++) break; /* error */
  38. continue;
  39. }
  40. format++;
  41. if (*format == '%') {
  42. ic = getc(stream);
  43. nrchars++;
  44. if (ic == '%') {
  45. format++;
  46. continue;
  47. }
  48. else break;
  49. }
  50. flags = 0;
  51. if (*format == '*') {
  52. format++;
  53. flags |= FL_NOASSIGN;
  54. }
  55. if (isdigit (*format)) {
  56. flags |= FL_WIDTHSPEC;
  57. for (width = 0; isdigit (*format);)
  58. width = width * 10 + *format++ - '0';
  59. }

  60. switch (*format) {
  61. case 'h': flags |= FL_SHORT; format++; break;
  62. case 'l': flags |= FL_LONG; format++; break;
  63. case 'L': flags |= FL_LONGDOUBLE; format++; break;
  64. }
  65. kind = *format;
  66. if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
  67. do {
  68. ic = getc(stream);
  69. nrchars++;
  70. } while (isspace(ic));
  71. if (ic == EOF) break; /* outer while */
  72. } else if (kind != 'n') { /* %c or %[ */
  73. ic = getc(stream);
  74. if (ic == EOF) break; /* outer while */
  75. nrchars++;
  76. }
  77. switch (kind) {
  78. default:
  79. /* not recognized, like %q */
  80. return conv || (ic != EOF) ? done : EOF;
  81. break;
  82. case 'n':
  83. if (!(flags & FL_NOASSIGN)) { /* silly, though */
  84. if (flags & FL_SHORT)
  85. *va_arg(ap, short *) = (short) nrchars;
  86. else if (flags & FL_LONG)
  87. *va_arg(ap, long *) = (long) nrchars;
  88. else
  89. *va_arg(ap, int *) = (int) nrchars;
  90. }
  91. break;
  92. case 'p': /* pointer */
  93. set_pointer(flags);
  94. /* fallthrough */
  95. case 'b': /* binary */
  96. case 'd': /* decimal */
  97. case 'i': /* general integer */
  98. case 'o': /* octal */
  99. case 'u': /* unsigned */
  100. case 'x': /* hexadecimal */
  101. case 'X': /* ditto */
  102. if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
  103. width = NUMLEN;
  104. if (!width) return done;

  105. str = o_collect(ic, stream, kind, width, &base);
  106. if (str < inp_buf
  107. || (str == inp_buf
  108. && (*str == '-'
  109. || *str == '+'))) return done;

  110. /*
  111. * Although the length of the number is str-inp_buf+1
  112. * we don't add the 1 since we counted it already
  113. */
  114. nrchars += str - inp_buf;

  115. if (!(flags & FL_NOASSIGN)) {
  116. if (kind == 'd' || kind == 'i')
  117. val = strtol(inp_buf, &tmp_string, base);
  118. else
  119. val = strtoul(inp_buf, &tmp_string, base);
  120. if (flags & FL_LONG)
  121. *va_arg(ap, unsigned long *) = (unsigned long) val;
  122. else if (flags & FL_SHORT)
  123. *va_arg(ap, unsigned short *) = (unsigned short) val;
  124. else
  125. *va_arg(ap, unsigned *) = (unsigned) val;
  126. }
  127. break;
  128. case 'c':
  129. if (!(flags & FL_WIDTHSPEC))
  130. width = 1;
  131. if (!(flags & FL_NOASSIGN))
  132. str = va_arg(ap, char *);
  133. if (!width) return done;

  134. while (width && ic != EOF) {
  135. if (!(flags & FL_NOASSIGN))
  136. *str++ = (char) ic;
  137. if (--width) {
  138. ic = getc(stream);
  139. nrchars++;
  140. }
  141. }

  142. if (width) {
  143. if (ic != EOF) ungetc(ic,stream);
  144. nrchars--;
  145. }
  146. break;
  147. case 's':
  148. if (!(flags & FL_WIDTHSPEC))
  149. width = 0xffff;
  150. if (!(flags & FL_NOASSIGN))
  151. str = va_arg(ap, char *);
  152. if (!width) return done;

  153. while (width && ic != EOF && !isspace(ic)) {
  154. if (!(flags & FL_NOASSIGN))
  155. *str++ = (char) ic;
  156. if (--width) {
  157. ic = getc(stream);
  158. nrchars++;
  159. }
  160. }
  161. /* terminate the string */
  162. if (!(flags & FL_NOASSIGN))
  163. *str = '\0';
  164. if (width) {
  165. if (ic != EOF) ungetc(ic,stream);
  166. nrchars--;
  167. }
  168. break;
  169. case '[':
  170. if (!(flags & FL_WIDTHSPEC))
  171. width = 0xffff;
  172. if (!width) return done;

  173. if ( *++format == '^' ) {
  174. reverse = 1;
  175. format++;
  176. } else
  177. reverse = 0;

  178. for (str = Xtable; str < &Xtable[NR_CHARS]
  179. ; str++)
  180. *str = 0;

  181. if (*format == ']') Xtable[*format++] = 1;

  182. while (*format && *format != ']') {
  183. Xtable[*format++] = 1;
  184. if (*format == '-') {
  185. format++;
  186. if (*format
  187. && *format != ']'
  188. && *(format) >= *(format -2)) {
  189. int c;

  190. for( c = *(format -2) + 1
  191. ; c <= *format ; c++)
  192. Xtable[c] = 1;
  193. format++;
  194. }
  195. else Xtable['-'] = 1;
  196. }
  197. }
  198. if (!*format) return done;

  199. if (!(Xtable[ic] ^ reverse)) {
  200. /* MAT 8/9/96 no match must return character */
  201. ungetc(ic, stream);
  202. return done;
  203. }

  204. if (!(flags & FL_NOASSIGN))
  205. str = va_arg(ap, char *);

  206. do {
  207. if (!(flags & FL_NOASSIGN))
  208. *str++ = (char) ic;
  209. if (--width) {
  210. ic = getc(stream);
  211. nrchars++;
  212. }
  213. } while (width && ic != EOF && (Xtable[ic] ^ reverse));

  214. if (width) {
  215. if (ic != EOF) ungetc(ic, stream);
  216. nrchars--;
  217. }
  218. if (!(flags & FL_NOASSIGN)) { /* terminate string */
  219. *str = '\0';
  220. }
  221. break;
  222. #ifndef NOFLOAT
  223. case 'e':
  224. case 'E':
  225. case 'f':
  226. case 'g':
  227. case 'G':
  228. if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
  229. width = NUMLEN;

  230. if (!width) return done;
  231. str = f_collect(ic, stream, width);

  232. if (str < inp_buf
  233. || (str == inp_buf
  234. && (*str == '-'
  235. || *str == '+'))) return done;

  236. /*
  237. * Although the length of the number is str-inp_buf+1
  238. * we don't add the 1 since we counted it already
  239. */
  240. nrchars += str - inp_buf;

  241. if (!(flags & FL_NOASSIGN)) {
  242. ld_val = strtod(inp_buf, &tmp_string);
  243. if (flags & FL_LONGDOUBLE)
  244. *va_arg(ap, long double *) = (long double) ld_val;
  245. else
  246. if (flags & FL_LONG)
  247. *va_arg(ap, double *) = (double) ld_val;
  248. else
  249. *va_arg(ap, float *) = (float) ld_val;
  250. }
  251. break;
  252. #endif
  253. } /* end switch */
  254. conv++;
  255. if (!(flags & FL_NOASSIGN) && kind != 'n') done++;
  256. format++;
  257. }
  258. return conv || (ic != EOF) ? done : EOF;
  259. }

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


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





作者: shane_west    时间: 2015-4-9 15:17
0.5 就是 0 啊,很定时死循环!!!!
作者: 落羽    时间: 2015-4-9 17:53
0.5 强制转换为整型为0 ; 所以一直循环
作者: senheima    时间: 2015-4-9 18:02
xiniuniu 发表于 2015-4-9 09:55
_doscan函数的实现源代码如下:在上面的源代码中,值得注意的是第26行的getc宏,定义代码如下:#define     ...

感谢,知道啦!!
作者: atmdada    时间: 2015-4-9 18:23
xiniuniu 发表于 2015-4-9 09:55
_doscan函数的实现源代码如下:在上面的源代码中,值得注意的是第26行的getc宏,定义代码如下:#define     ...

大神啊  小白表示目前还看不懂,只会简单地使用scanf函数
作者: 綦敦涛    时间: 2015-4-9 19:19
因为你scanf的格式控制符是%d,所以尽管你输入的时0.5,实际存入n的值是0,这样n<=0会一直成立,所有是无限循环
作者: senheima    时间: 2015-4-9 20:50
綦敦涛 发表于 2015-4-9 19:19
因为你scanf的格式控制符是%d,所以尽管你输入的时0.5,实际存入n的值是0,这样n

多谢 回答了!!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2