【问题标题】:Why is stringstream mixing up it's content in combination with char array and cout?为什么 stringstream 将其内容与 char 数组和 cout 结合起来?
【发布时间】:2021-07-27 08:55:28
【问题描述】:

在这个最小的例子中,字符串流的输入和以前使用的 cout 的内容之间存在奇怪的混乱:

在线 gdb: https://onlinegdb.com/itO69QGAE

代码:

#include <string>
#include <iostream>
#include <sstream>

using namespace std;

const char sepa[] = {':', ' '};
const char crlf[] = {'\r', '\n'};


int main()
{
    cout<<"Hello World"  << endl;

    stringstream s;
    
    string test1 = "test_01";
    string test2 = "test_02";


    s << test1;
    cout << s.str() << endl;
    // works as expected
    // excpecting: "test_01"
    // output: "test_01"
    
    s << sepa;
    cout << s.str() << endl;
    // messing up with previous cout output
    // expecting: "test_01: "
    // output: "test_01: \nHello World"
    
    s << test2;
    cout << s.str() << endl;
    // s seems to be polluted
    // expecting: "test_01: test_02"
    // output:  "test_01: \nHello Worldtest_02"
    
    s << crlf;
    cout << s.str() << endl;
    // once again messing up with the cout content
    // expecting: "test_01: test_02\r\n"
    // output: "test_01: Hello Worldtest_02\r\nHello World"

    return 0;
}

所以我想知道为什么会这样?

因为它仅在将 char 数组推入字符串流时发生,所以很可能与此有关……但根据参考,字符串流的“

除此之外,stringstream 和 cout 之间似乎存在(?隐藏的,或者至少不明显的?)关系。那么为什么内容会污染到字符串流中呢?

在这个例子中是否有任何错误/愚蠢的用法或者狗被埋在哪里(-> 德语成语 :P )?

最好的问候和感谢 达米安

附:我的问题不是关于“解决”这个问题,比如使用字符串而不是 char 数组(这将起作用)......它是关于理解内部机制以及为什么这实际上会发生,因为对我来说这只是一个意想不到的行为。

【问题讨论】:

  • 对于s &lt;&lt; sepa;,您将sepa 视为以空字符结尾的字符串。它不是,所以你有未定义的行为。与s &lt;&lt; crlf; 相同。为什么不制作 sepacrlf 实际字符串?或者甚至使用 s &lt;&lt; ": ";s &lt;&lt; "\r\n"; 中的文字字符串?
  • @Someprogrammerdude 给出了答案 - 还包括这种未定义行为的实际效果

标签: c++ cout stringstream


【解决方案1】:

std::stringstream::str() 函数在对operator&lt;&lt;(或其他输出函数)的所有先前调用中返回一个字符串,其中包含之前写入的所有字符到流中。但是,您似乎希望只返回最后一个输出操作 - 事实并非如此。

这类似于例如std::cout 有效:每次调用 std::cout &lt;&lt; 都会将字符串附加到标准输出;它不会清除控制台的屏幕。

要实现您想要的,您要么每次都需要使用单独的std::stringstream 实例:

std::stringstream s1;
s1 << test1;
std::cout << s1.str() << std::endl;

std::stringstream s2;
s2 << sepa;
std::cout << s2.str() << std::endl;

或者更好的是,使用str() 函数的单参数重载清除std::stringstream 的内容:

std::stringstream s;

s << test1;
std::cout << s.str() << std::endl;

// reset the contents of s to an empty string
s.str(""); 
s << sepa;
std::cout << s.str() << std::endl;

s.str("") 调用有效地丢弃了之前写入流中的所有字符。 请注意,即使 std::stringstream 包含一个看起来更好的候选函数 clear() ,它也不类似于例如std::string::clear()std::vector::clear() 不会产生您的情况所需的效果。

【讨论】:

  • 那么“Hello World”从何而来?
【解决方案2】:

我又来了,

感谢“一些程序员老兄”的评论,我想我明白了:

由于没有与两个 char 数组相关的(null-)终止符号,似乎 stringstream-&lt;&lt;-operator 插入,直到它偶然发现一个空终止符 '\0'

要么用\0-symbol(例如const char sepa[] = {':', ' ', '\0'})扩展两个数组,要么用例如终止长度。 s &lt;&lt; string(sepa,2) 将执行预期的输出。

在上述特定情况下,数据似乎在内存中对齐,因此下一个空终止符将在cout &lt;&lt; "Hello World"-语句中找到。由于无法保证这种对齐方式,当终止缺失时,这实际上会导致未定义的行为。

所以还有两个额外的“终止”数组,例如 const char sepa[] = {':', ' '}; char[] end_of_sepa = {'\0'}; 在提到的数组之后声明将导致预期的输出,即使其余部分保持不变...但这可能无法保证,并且取决于内存中的内部表示。

附:如前所述,这个问题不是关于修复而是理解。因此,请随时确认或更正我的假设。

编辑:更正了粗体代码部分。

【讨论】:

  • 它会导致未定义的行为 - 在您建议在其他内存空间中添加其他变量时,该行为仍然未定义。一个不错的选择是在 sepa 本身的末尾添加空终止符,例如char sepa[] = ": ";
猜你喜欢
  • 2010-10-04
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多