在论坛上看到有人提出一个如下的问题,在此总结一下。

原问题:

http://topic.csdn.net/u/20110414/22/90d0606c-9876-48e4-9b69-bd8bd8a41897.html

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int ival1 = 123, ival2 = 1;
  5. char ch = 't';
  6. scanf("%d%d", &ival1, &ival2);
  7. scanf("%c", &ch);
  8. printf("%d/n%d/n%c/n", ival1, ival2, ch);
  9. return 0;
  10. }
  11. /*
  12. 输入:2回车a回车
  13. 结果是:
  14. 2
  15. 1
  16. a
  17. */

#include <stdio.h>
int main()
{
int ival1 = 123, ival2 = 1;
char ch = 't';
scanf("%d%d", &ival1, &ival2);
scanf("%c", &ch);
printf("%d/n%d/n%c/n", ival1, ival2, ch);
return 0;
}
/*
输入:2回车a回车
结果是:
2
1
a
*/

LZ的疑问是 :但是第一个回车符还在缓冲区中啊! 为什么它没有被变量 ch 读走?

我将上述的问题再详细描述一下:即,输入完变量 ival1 之后,输入缓冲区中现在存留一个换行符 '/n',然后此时又从键盘上输入一个字符a,LZ的问题是,为什么变量 ch 没有将前面的换行符 '/n' 读走,而是跳过却将后面输入的字符a读走了。





问题解释



在很多词法/语法分析器中,空白字符(whitespace)指的是:任意多个基本空白字符(包括空格、制表符/t、回车符/r、换行符/n等)的连续组合。注意,在C/C++编程语言中,C风格的字符串又叫null-terminated string,一般译为空字符结尾的字符串,这里空字符(注意,不叫空白字符)是指'/0',或者是字符的编码值为0的字符。C的标准库中有一个函数,isspace(char c)函数可以检测一个字符是否为空白符,需要包含cctype头文件。



看 scanf 的两种情况 :



Case 1: 输入完ival1之后,不管输入多少个 whitespace,都会继续等待输入ival2,原因是:此时的 whitespace 被视为输入的分隔符,即,会被忽略。

  1. int ival1, ival2;
  2. scanf("%d", &ival1);
  3. scanf("%d", &ival2);

int ival1, ival2;
scanf("%d", &ival1);
scanf("%d", &ival2);



Case 2: 输入完ival之后,变量ch也会从stdin流中读取数据,输出显示 ch 的ASCII码值为10,即,为换行符 '/n'。而换行符属于 whitespace,本应该起到分隔符的作用,但是这里却当成有效字符读入到变量 ch 中。

  1. int ival;
  2. char ch;
  3. scanf("%d", &ival);
  4. scanf("%c", &ch);

int ival;
char ch;
scanf("%d", &ival);
scanf("%c", &ch);



再对比下 cin 的两种情况 :(主要注意 Case 2 )



Case 1: 输入完ival1之后,不管输入多少个 whitespace,都会继续等待输入ival2,原因是:此时的 whitespace 被视为输入的分隔符,即,会被忽略。

  1. int ival1 = 0, ival2 = 0;
  2. cin >> ival1;
  3. cin >> ival2;

int ival1 = 0, ival2 = 0;
cin >> ival1;
cin >> ival2;



Case 2: 输入完ival之后,不管输入多少个 whitespace,都会继续等待输入 ch ,这里空白符依然被忽略。注意,这里与 scanf 效果不同!

  1. int ival = 0;
  2. char ch;
  3. cin >> ival;
  4. cin >> ch;

int ival = 0;
char ch;
cin >> ival;
cin >> ch;



结论

(1) 当 scanf 中的输入格式说明符 (Format specifiers) 不是 "%c"的时候,那么空白字符 (whitespace) 将起到分隔符的作用。把分隔好的两个数据分别赋值到各自定义好的变量或数组中去,两个数据之间的 whitespace 被从缓冲区读出但是不起任何作用,当然最后一个 '/n' 会被留在缓冲区内,除非用 getchar(); 或 scanf("%c",&ch); 把它读出来。

(2) 当 scanf 中的输入格式说明符 (Format specifiers) 是 "%c"的时候,那么空白字符 (whitespace) 将会被正常读入,不再起到分隔符的作用。

(3) 使用 cin 的时候,空白字符都会当成分隔符而被忽略。



注意

(1) 当按键盘上的回车键将产生了2个字符:回车符('/r')和换行符('/n')。回车符'/r' (CR:carriage return:倒车) 使光标回到这行的首部,换行符('/n') (new line) 然后再换行。

(2) 回车是一定要有的,不管是 getchar(); 还是 scanf 只要是通过缓冲区输入数据的函数都是等待回车符 '/r' 出现才进入缓冲区的。



Format specifiers:

 A sequence formed by an initial percentage sign (%) indicates a format specifier, which is used to specify the type and format of the data to be retrieved from stdin and stored in the locations pointed by the additional arguments. A format specifier follows
this prototype:

%[*][width][modifiers]type

再看看LZ的问题



LZ的问题是,为什么变量 ch 没有将前面的换行符 '/n' 读走,而是跳过却将后面输入的字符a读走了。

原因就是:读取ival1成功之后,下一个要读取的数据格式是 "%d",所以前面存留的换行符 '/n' 被忽略了,因此读取的是下个输入的非空白字符 'a',即,变量 ch 的值为 'a'。

这里还要注意:变量 b 没有读到合法的数据,因此,变量 b 仍然是以前的初始值。通过 scanf 的返回值可以判断成功读取(从键盘输入)数据的个数。

scanf 版

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cctype>
  4. int main()
  5. {
  6. int ival1 = 0, ival2 = 0;
  7. char ch = 't';
  8. char ch_tmp;
  9. scanf("%d", &ival1);
  10. scanf("%c", &ch_tmp);
  11. if (isspace(ch_tmp))
  12. {
  13. printf("%d is a whitespace right ? %d is the newline character!/n", ch_tmp, ch_tmp);
  14. }
  15. if (scanf("%d", &ival2) != 1)
  16. {
  17. printf("Damn it! ival2 cannot read input!/n");
  18. }
  19. scanf("%c", &ch);
  20. printf("%d %d %c/n", ival1, ival2, ch);
  21. system("pause");
  22. return 0;
  23. }

#include <cstdio>
#include <cstdlib>
#include <cctype>
int main()
{
int ival1 = 0, ival2 = 0;
char ch = 't';
char ch_tmp;
scanf("%d", &ival1);
scanf("%c", &ch_tmp);
if (isspace(ch_tmp))
{
printf("%d is a whitespace right ? %d is the newline character!/n", ch_tmp, ch_tmp);
}
if (scanf("%d", &ival2) != 1)
{
printf("Damn it! ival2 cannot read input!/n");
}
scanf("%c", &ch);
printf("%d %d %c/n", ival1, ival2, ch);
system("pause");
return 0;
}

cin 版

  1. #include <iostream>
  2. #include <cctype>
  3. using namespace std;
  4. int main()
  5. {
  6. int ival1 = 0, ival2 = 0;
  7. char ch = 't';
  8. char ch_tmp;
  9. cin >> ival1;// 读取并忽略有效字符之前所有的空白字符,然后读取字符直至再次遇到空白字符,读取终止,该空白字符仍留在输入流中
  10. cin >> ch_tmp;// 注意:这里与scanf的区别,此处将空白字符忽略,而不是作为有效字符
  11. if (isspace(ch_tmp))
  12. {
  13. cout << ch_tmp << " is a whitespace right ?" << ch_tmp << " is the newline character!" << endl;
  14. }
  15. cin >> ival2;
  16. if (! cin)
  17. {
  18. cout << "Damn it! ival2 cannot read input!" << endl;
  19. }
  20. cin >> ch;
  21. cout << ival1 << " " << ival2 << " " << ch;
  22. system("pause");
  23. return 0;
  24. }

#include <iostream>
#include <cctype>
using namespace std;
int main()
{
int ival1 = 0, ival2 = 0;
char ch = 't';
char ch_tmp;
cin >> ival1;// 读取并忽略有效字符之前所有的空白字符,然后读取字符直至再次遇到空白字符,读取终止,该空白字符仍留在输入流中
cin >> ch_tmp;// 注意:这里与scanf的区别,此处将空白字符忽略,而不是作为有效字符
if (isspace(ch_tmp))
{
cout << ch_tmp << " is a whitespace right ?" << ch_tmp << " is the newline character!" << endl;
}
cin >> ival2;
if (! cin)
{
cout << "Damn it! ival2 cannot read input!" << endl;
}
cin >> ch;
cout << ival1 << " " << ival2 << " " << ch;
system("pause");
return 0;
}

再对 cin 版验证一下:

  1. #include <iostream>
  2. #include <cctype>
  3. using namespace std;
  4. int main()
  5. {
  6. int ival1 = 0, ival2 = 0;
  7. char ch = 't';
  8. char ch_tmp;
  9. streambuf* sb;
  10. sb = cin.rdbuf();
  11. cout << sb->in_avail() << endl;// will print 0
  12. cin >> ival1;// 读取并忽略有效字符之前所有的空白字符,然后读取字符直至再次遇到空白字符,读取终止,该空白字符仍留在输入流中
  13. cout << sb->in_avail() << endl;// will print 1 证明空白字符'/n' 确实留在输入缓冲区中
  14. cin >> ch_tmp;// 注意:这里与scanf的区别,此处将空白字符忽略,而不是作为有效字符
  15. if (isspace(ch_tmp))
  16. {
  17. cout << ch_tmp << " is a whitespace right ?" << ch_tmp << " is the newline character!" << endl;
  18. }
  19. cin >> ival2;
  20. if (! cin)
  21. {
  22. cout << "Damn it! ival2 cannot read input!" << endl;
  23. }
  24. cin >> ch;
  25. cout << ival1 << " " << ival2 << " " << ch;
  26. system("pause");
  27. return 0;
  28. }

#include <iostream>
#include <cctype>
using namespace std;
int main()
{
int ival1 = 0, ival2 = 0;
char ch = 't';
char ch_tmp;
streambuf* sb;
sb = cin.rdbuf();
cout << sb->in_avail() << endl;// will print 0
cin >> ival1;// 读取并忽略有效字符之前所有的空白字符,然后读取字符直至再次遇到空白字符,读取终止,该空白字符仍留在输入流中
cout << sb->in_avail() << endl;// will print 1 证明空白字符'/n' 确实留在输入缓冲区中
cin >> ch_tmp;// 注意:这里与scanf的区别,此处将空白字符忽略,而不是作为有效字符
if (isspace(ch_tmp))
{
cout << ch_tmp << " is a whitespace right ?" << ch_tmp << " is the newline character!" << endl;
}
cin >> ival2;
if (! cin)
{
cout << "Damn it! ival2 cannot read input!" << endl;
}
cin >> ch;
cout << ival1 << " " << ival2 << " " << ch;
system("pause");
return 0;
}

参考

scanf

http://www.cplusplus.com/reference/clibrary/cstdio/scanf/

getchar,scanf以及缓冲区的概念

http://blog.csdn.net/weinixugeyuan/archive/2009/03/12/3980498.aspx

getch()、getche()和getchar()之间的区别

http://www.cnitblog.com/mantou/archive/2010/01/13/1250.html

ios::rdbuf

http://www.cplusplus.com/reference/iostream/ios/rdbuf/

http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/57d90614-8555-488c-acc4-f53fb04baaa3

scanf 与 cin 的区别的更多相关文章

  1. scanf 和cin 的区别

    笔试的时候经常遇到突然string s;cin>>s; 有的时候编译会错误,不知道为什么. 今天在练习枚举类型的时候,也遇到这样一个问题. enum weekday{Monday,Tues ...

  2. scanf和cin的差异

    scanf和cin的差异 引例:http://www.cnblogs.com/shenben/p/5516996.html 大家都知道,在C++中有两种输入.输出方式—scanf和cin,但是,它们之 ...

  3. 关于scanf与cin哪个快的问题

    一开始入c++的时候成天跑cin,cout 直到有一天用cin,cout超时 才知道scanf比cin快的多 但是后来又听说加了ios::sync_with_stdio(false);的cin跟飞一样 ...

  4. scanf()与gets()的区别

    scanf()与gets()的区别 1.scanf()可以同时接受多个字符串,而gets()一次只能接受一个字符串. #include<stdio.h>int main(){ char s ...

  5. scanf,fscanf,sscanf的区别----整理

    转自原文 scanf,fscanf,sscanf的区别----整理 scanf 从控制台输入 fscanf 从文件输入 sscanf 从指定字符串输入 1.例:使用scanf函数输入数据. #incl ...

  6. 关于scanf与gets的区别

    以下内容主要来源: scanf与gets读取字符串 scanf与gets函数读取字符串的区别 前两天有个同学问我scanf与gets的区别说了半天也没说出来个所以然,就搜了一下,scanf()和get ...

  7. 关于scanf 与 cin gets(),getline()......输入输出字符串的区别

    很对人对于字符串的输入输出一直是比较模糊的,今天总结一下几个常用的输入流符号对于输入字符串时的区别: 1.scanf(),首先 它遇到空格或回车键(\n)就会结束,并且会将回车符算入字符串中: 2.c ...

  8. scanf函数和cin的区别、类的数组、C++排序函数

    给定n个字符串,将这n个字符串按照字典序进行排列,此处用排列函数是C++的库函数sort,产生如下两个疑问,望大佬解答 #include <iostream> #include <a ...

  9. Java中的“scanf()、cin()、input()"

    最近在写一个Java程序时遇到一个问题,就是如何在Java里面输入数值,又叫做获取键盘输入值. 因为c语言里面有scanf(),C++里面有cin(),python里面有input().Java里面有 ...

随机推荐

  1. Net Core- 配置组件

    Net Core- 配置组件 我们之前写的配置都是放置在配置文件Web.config或者app.config中,.net core提供了全新的配置方式,可以直接写在内存中或者写在文件中. .Net C ...

  2. SQL Server 数据类型陷阱

    1. bit 类型:bit(1) 不要以为它只占一个位,事实上它要占一个字节!也就是说当n < 8 时都是这样的! 2. varchar(n)  这里的n不能大于8000,如果想要比8000大你 ...

  3. class类的sizeof计算

    class no_virtual { public: void fun1() const{} int fun2() const { return a; } private: int a; } clas ...

  4. 我的MYSQL学习心得 mysql日志

    这一篇<我的MYSQL学习心得(十五)>将会讲解MYSQL的日志 MYSQL里的日志主要分为4类,使用这些日志文件,可以查看MYSQL内部发生的事情. 分别是 1.错误日志:记录mysql ...

  5. ubuntu apache fastcgi 虚拟主机安装

    1 cp /etc/apache2/sites-available/default /etc/apache2/sites-available/www.domain.com 这里www.domain.c ...

  6. hdu 4738 Caocao's Bridges(2013杭州网络赛丶神坑)

    就是求最小权值的桥..不过有好几个坑... 1:原图不连通,ans=0. 2: m<=n^2 显然有重边,重边必然不是桥,处理重边直接add(u, v, INF). 3:   最小桥边权为0的时 ...

  7. MyEclipse中jsp的凝视报错解决

    jsp页面中凝视报错: 出错现场:在eclipse中没有报错.在MyEclipse中报错. <!-- To use express install, set to playerProductIn ...

  8. c/c++测试程序运行时间

    算法分析中需要对各种算法进行性能测试,下面介绍两种通用的测试方法,由于只用到标准c语言函数,所以在各种平台和编译器下都能使用. 方法1: clock()函数 开始计时:start = clock() ...

  9. 23种设计模式的C++实现

    之前看Head First设计模式的时候照着书上的代码实现了一个C++版本(书上是Java版本的),代码上传在https://github.com/clpsz/Book-HFDP-Code. 当时因为 ...

  10. 采用translate实现垂直水平居中

    今天分享一个利用css3新特性实现垂直水平居中的方法. 通过对元素进行绝对定位再配合transform中的translate实现. 代码如下: html <div id="conten ...