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

Puzzling behavior of istream::getline()

本文关键字:令人费解 getline istream      更新时间:2023-10-16

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

#include <iostream>
#include <sstream>
using namespace std;
int main()    
{
string s("abcd efghnijklmnopqrst");         
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

此外,我发现呼叫ss.getline()s1的大小与呼叫ss>>后的大小相同,但是再次呼叫ss>>后大小会发生变化。谁能帮我解析?

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

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

使整个事件复杂化的事实是,这部std::string给人的印象是它包含四个角色。因为这就是它所拥有的一切,在这一点上。

绝对没有任何可能使这个std::string改变主意。仅仅因为指向其内部字符缓冲区的指针被传递给getline(),它继续相当粗鲁地涂鸦(导致未定义的行为,正如我稍后会推断的那样),std::string仍然认为它只包含四个字符。

同时,初始格式化输入运算符>>提取了初始字符,但没有提取以下空格,因此当此流随后具有此getline()调用时,它开始提取从此空格字符开始的字符的工作,直到下一个换行符 - 五个字符(如果我用手指计算), 但是将其转储到缓冲区中,该缓冲区由std::string保证,仅足够长以容纳四个字符(因为请记住,初始格式化的提取运算符>>仅在其中转储了四个字符)。

我忽略了一些细节,例如std::string负责自动跟踪尾随'',但最重要的是这是未定义的行为。getline 调用提取更多给定的缓冲区保证包含的字符。未定义的行为。一大堆未定义的行为。不仅仅是你第二行输出中的四个字符不是你期望看到的四个字符,只是getline()实际上最终提取了更多的字符,但这里印刷的std::string根据宪法完全有权相信它仍然只有四个字符, 只是它的内部缓冲区被踩得满地都是。

两件事。

首先,>>不消耗空格,因此getline将检索它。

其次,这条线不正确:

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

由于getline期望一个std::basic_string,只需传入字符串:

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

在代码中,&s1[0]可以访问基础缓冲区,该缓冲区被写入,但字符串的长度是单独存储的,并且仍然是上次读取的长度(这就是删除h的原因)。但是,此时您已经由于缓冲区溢出而调用了未定义的行为。