Puzzling behavior of istream::getline()

别等时光非礼了梦想. 提交于 2019-12-24 21:42:40

问题


I tested following codes to clarify my understanding to 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;
        }

then the console printed:

abcd
 efg
ijklmnopqrst

but in my opinon it should be

abcd
efgh
ijklmnopqrst

Besides, I found the size of s1 after calling ss.getline() was the same as that after calling ss>>, but the size will be changed after calling ss>> once more. Can anyone help me parsing?


回答1:


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

The first parameter to this getline() call is a char *. ss knows absolutely nothing about the fact that this char buffer actually comes from a std::string, and its actually its internal buffer.

Complicating this entire affair is the fact that this std::string is under the impression that it contains four characters. Because that's all it has, at this point.

And there is absolutely nothing, whatsoever, that could possibly lead this std::string to change its mind. Just because a pointer to its internal character buffer was passed to getline(), which proceeded to rather rudely scribble all over it (resulting in undefined behavior, as I'll extrapolate in a moment), the std::string still believes that it contains four characters only.

Meanwhile, the initial formatted input operator, >> extracted the initial character, but did not extract the following space, so when this stream, subsequently, had this getline() call, it started off its job of extracting characters starting with this space character, and up until the next newline character -- five characters (if I count on my fingers), but dumping it into a buffer that's guaranteed, by the std::string, to only be long enough to hold four characters (because, keep in mind, the initial formatted extract operator, >>, only dumped four characters inside it).

I'm ignoring some details, such as the fact that std::string takes care of automatically tacking on a trailing '\0', but the bottom line is that this is undefined behavior. The getline call extracts more characters that the buffer it's given is guaranteed to hold. Undefined behavior. A whole big heap of undefined behavior. It's not just the four characters in your second line of output is not the four characters you were expected to see, it's just that the getline() actually ended up extracting more characters, but the std::string that's being printed here has every right under the constitution to believe that it still has only four characters, and it's just that it's internal buffer got stomped all over.




回答2:


Two things.

First, >> does not consume whitespace, so getline will retrieve it.

Secondly, this line is not correct:

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

Since getline expects a std::basic_string, just pass in the string:

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

In your code, &s1[0] gets access to the underlying buffer, which is written to, but the string's length is stored separately, and is still what is was from the previous read (which is why the h gets dropped). Though, at this point you've already invoked undefined behaviour due to a buffer overflow.



来源:https://stackoverflow.com/questions/48413784/puzzling-behavior-of-istreamgetline

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!