istream :: getline()的令人费解的行为

时间:2018-01-24 02:08:55

标签: c++ iostream getline

我测试了以下代码,以澄清我对istream::getline()的理解:

 #include <iostream>
    #include <sstream>
    using namespace std;
        int main()    
        {
           string s("abcd efgh\nijklmnopqrst");         
           string s1;
           stringstream ss(s);
           ss >> s1;
           cout << s1 << endl;
           ss.getline(&s1[0], 250, '\n');
           cout << s1 << endl;
           ss >> s1;
           cout << s1 << endl;
           getchar();
        return 1;
        }
然后控制台打印出来:

abcd
 efg
ijklmnopqrst

但在我看来应该是

abcd
efgh
ijklmnopqrst

此外,我在调用s1后发现ss.getline()的大小与调用ss>>后的大小相同,但在再次调用ss>>之后,大小将会更改。任何人都可以帮我解析吗?

2 个答案:

答案 0 :(得分:3)

两件事。

首先,>>不占用空格,因此getline会检索它。

其次,这条线不正确:

ss.getline(&s1[0], 250, '\n');

由于getline需要std::basic_string,只需传入字符串:

ss.getline(s1, 250, '\n');

在你的代码中,&s1[0]可以访问写入的底层缓冲区,但是字符串的长度是单独存储的,并且仍然是前一次读取的内容(这就是为什么h被删除了。但是,此时由于缓冲区溢出,您已经调用了未定义的行为。

答案 1 :(得分:3)

ss.getline(&s1[0], 250, '\n');

getline()来电的第一个参数是char *ss绝对不知道这个char缓冲区实际上来自std::string,它实际上是它的内部缓冲区。

使整个事件变得复杂的是,这std::string的印象是它包含四个字符。因为这就是它的全部。

绝对没有什么可能导致这个std::string改变主意。只是因为指向其内部字符缓冲区的指针被传递给getline(),后者继续粗略地乱写它(导致未定义的行为,因为我将在片刻中推断),std::string仍然认为它只包含四个字符。

同时,初始格式化输入操作符>>提取了初始字符,但 没有 提取以下空格,所以当这个流后来,有了这个getline()调用,它开始从这个空格字符开始提取字符的工作,直到下一个换行符 - 五个字符(如果我依靠我的手指),但将其转储到缓冲区中std::string保证只能保留四个字符(因此,请记住,初始格式化的提取运算符>>,只能在其中转储四个字符)。

我忽略了一些细节,例如std::string负责自动处理尾随'\0',但底线是这是未定义的行为。 getline调用会提取更多字符,保证它所保留的缓冲区。未定义的行为。一大堆未定义的行为。不仅仅是你的第二行输出中的四个字符不是你期望看到的四个字符,只是getline()实际上最终提取 更多 字符,但是在这里打印的std::string在宪法下完全相信它仍然只有四个字符,只是它的内部缓冲区全部被踩了。