正确理解cin.sync()&cin>>&cin.getline()

分类 - 代码 共有 2 条评论

首先向大家介绍一下以下函数:

1.cin.clear()
2.cin.sync()
3.cin.ignore()
4.cin.getline()
5.cin.get()
  1. cin.clear():这个函数是用来设置cin的错误状态标志的。(Set error state flags)
  2. cin.sync():这个函数是用来同步输入缓冲区的。(Synchronize input buffer)
  3. cin.ignore():这个函数是用来忽略字符的。(Extract and discard characters)它的原型为:

    std::basic_istream<char,std::char_traits<char>> &ignore(std::streamsize _Count,int _Metadelim)
    

从这里可以可以看出这个函数接收一个大小,和一个可以用int表示的单个字符。这个函数的作用是从输入流(cin)提取字符,每提取一个字符计数器+1,直到计数器达到_Count或者遇到_Metadelim,提取到的所有字符被忽略(包括_Metadelim),不被使用。

  1. cin.getline():这个函数是用来从键盘获取字符的。它与 C 语言里的 gets()函数类似,它的功能是能从输入流中读取一段带空白字符的字符串,只有遇到回车符才结束。它的一个重载的原型为:

    std::basic_istream<char,std::char_traits<char>> &getline(char *_Str,std::streamsize _Count) 
    

从这里可以看出这个函数接收的参数为(某个字符串的首地址,字符串的大小)

注意不要混淆cin.getline()与getline,这是两个不同的函数。具体细节这里就不再赘述,请大家自行查阅相关资料。

  1. cin.get():这个函数的其中一个重载函数的作用是从输入流读取一个字符并将其返回。

请大家看一段程序:

#include<iostream>
using namespace std;
int main()
{
    int time = 0;
    char words[20];
    char ala[20];
    while (1)
    {
        time++;
        cin.getline(words,19);
        cin >> ala;
        cout <<time<<":"<< words<<endl;
    }
    return 0;
}

在cout前面加入断点测试程序:
duandian

第一次循环变量监测:
bianliang

断点截屏:
screen

shuju2

第二次循环变量监测:
bianliang2
运行结果:

ceshi1

大家可以看到此程序第二次循环当中words 的值为空,而ala的值为 LovelyWhite
这说明cin.getline()被跳过了!
但这是为什么呢?
查阅cin>>cin.getline()的工作原理,我们可以得知:
流提取运算符(析取器)根据它后面的变量类型读取数据,从非空白符号开始,遇到Enter、Space、Tab等空白字符时结束,cin不会接收空白字符,而是选择跳过它,并将空白字符保留在缓冲区,而cin.getline()能够接收空白字符,所以导致第二次循环cin>>从键盘获取数据后留下了一个回车符在缓冲区,并将它给了cin.getline()。所以导致图中所显示的words为空的情况。

解决方案:

只需要将缓冲区当前的回车符去掉就可以使程序正常运行。

具体实现方案:

1.使用cin.get()接收缓冲区的回车符
2.使用cin.sync()&cin.clear()清空输入缓冲区(至于为什么要搭配请参阅心痕的博客)
3.使用cin.ignore(1024,'\n')通过把第一个参数设置得足够大,所以实际上上总是只有第二个参数'\n'起作用,所以这一句就是把回车(包括回车)之前的所有字符从输入缓冲流中清除出去。

通过一系列测试,我发现2.cin.sync()&cin.clear() 在不同的平台上并不能总是达到想要实现的效果。也就是说sync()并不能总是能够清空输入缓冲区。通过查阅资料发现(我用的是Visual Studio 2017),我使用的平台不能使用sync()清空缓冲区。进一步了解,sync是同步的意思,并没有清空的意思。

通过以下程序进行测试:

#include <iostream>
using namespace std;
int main()
{
    char first, second;
    first = cin.get();
    cin.sync();
    second = cin.get();
    cout << first << endl;
    cout << second << endl;
    system("pause");
    return 0;
}

注:两次输入都为text

在CodeBlock中测试的结果:
text1

在Visual Studio 2017中测试的结果:
text2

分析(Vs环境下):
首先明确:输入设备流与输入缓冲区不是一个概念。
程序一开始从键盘获取了text,接着按下回车,此时输入设备流的内容为text,输入缓冲区的内容为text,然后cin.get()将第一个字符 t 赋给了first,同时从缓冲区中删除t,这时流定位向下移动一位(e的位置)。这时,输入缓冲区的内容变为 ext ,输入设备流的内容依然为:text 。然后进入了cin.sync() 这个函数,同步输入设备流与输入缓冲区的内容,输入缓冲区的内容又变为了text,此时的流的定位位置为 e 。cin.get()获得了 e ,所以最后输出的字符为 e 。从而造成了跳过了cin.get()的错觉。

那为什么在CB的编译器下,cin.sync()就能清除缓冲区内容呢?
分析(CB环境下):
因为在CB下,输入设备流在把数据赋给输入缓冲区后,就清空了输入设备流的内容,在cin.sync()的同步下,输入缓冲区被同步了空内容,第二次的cin.get()由于输入缓冲区没有内容,所以将从键盘(输入设备流)输入数据,在这个操作下,流的定位位置会被刷新,所以cin.get()的到的是t,输出的也就是t啦。

结语

看到这里,大家应该已经深刻了解了getline(),cin>>,sync()的作用。在写程序的时候一定要多细心,多耐心,多查阅资料。
如果你有好的想法,意见或者建议欢迎在评论区留言~

查阅的资料:

  1. C++ Reference
  2. 心痕的博客
文章评论
  1. 666

    666

    回复
    1. @666

      666

      回复