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

Puzzling behavior of istream::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

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

两件事。

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

其次,这行不正确:

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

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

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

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

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

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

使整个事件复杂化的是这个 std::string 给人的印象是它包含四个字符。因为这就是它的全部,在这一点上。

绝对没有任何事情可以导致 std::string 改变主意。仅仅因为一个指向其内部字符缓冲区的指针被传递给 getline(),它开始相当粗鲁地在它上面涂鸦(导致未定义的行为,我稍后会推断),std::string 仍然认为它只包含四个字符。

同时,初始格式化输入运算符,>>提取了初始字符,但是没有提取下面的space,所以当这个流随后有这个 getline() 调用时,它开始提取以这个 space 字符开始的字符,直到下一个换行符——五个字符(如果我指望我的话),但是将它转储到一个缓冲区中,该缓冲区由 std::string 保证,其长度仅足以容纳四个字符(因为,请记住,初始格式化提取运算符 >>, 里面只转储了四个字符).

我忽略了一些细节,例如 std::string 负责自动跟踪尾随 '[=25=]',但底线是这是未定义的行为。 getline 调用提取它所提供的缓冲区保证容纳的更多字符。未定义的行为。一大堆未定义的行为。这不仅仅是你第二行输出中的四个字符不是你期望看到的四个字符,只是 getline() 实际上最终提取了 more 个字符,但是这里打印的 std::string 根据宪法完全有权利相信它仍然只有四个字符,只是它的内部缓冲区被踩得一塌糊涂。