为什么 stringstream 将其内容与 char 数组和 cout 混合在一起?

问题描述

在这个最小的例子中,stringstream 的输入和之前使用的 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 时,它很可能是关于这个......但根据参考,stringstream 的“

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

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

最好的问候和感谢 达米安

附言我的问题不是关于“修复”这个问题,比如使用字符串而不是字符数组(这会起作用)……这是关于理解内部机制以及为什么这实际上会发生,因为对我来说这只是一种意外行为。

解决方法

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

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

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

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

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

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

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() 并且不会产生您的情况所需的效果。

,

我又来了,

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

由于没有与两个 char 数组相关的 (null-)termination-symbol,因此 stringstream-<<-operator 似乎会一直插入,直到它遇到空终止符 '\0'

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

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

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

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

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