C++:为什么这个字符串输入失败而另一个没有

C++: Why does this string input fail while the other does not

我从朋友那里得到了这个问题

#include <string>
#include <vector>
#include <iostream>

void riddle(std::string input)
{
    auto strings = std::vector<std::string>{};
    strings.push_back(input);
    auto raw = strings[0].c_str();

    strings.emplace_back("dummy");

    std::cout << raw << "\n";
}

int main()
{
    riddle("Hello world of!"); // Why does this print garbage?
    //riddle("Always look at the bright side of life!"); // And why doesn't this?

    std::cin.get();
}

我的第一个观察是,当传入input的字数超过3个字时,riddle()函数不会产生垃圾。我仍在尝试了解为什么第一种情况失败而不是第二种情况。不管怎样,我觉得分享这件事很有趣。

这是未定义的行为 (UB),意味着任何事情都可能发生,包括代码工作。 它是 UB,因为 emplace_back 使指向向量中对象的所有指针无效。发生这种情况是因为向量可能会被重新分配(显然是这样)。

UB第一例"doesn't work"因为短字符串优化(sso)。由于sso,原始指针指向vector直接分配的内存,重新分配后丢失。

UB的第二种情况"works"因为字符串文本对于SSO来说太长,驻留在一个独立的内存块上。在调整大小时,字符串对象被移动,将文本内存块的所有权移动到新创建的字符串对象。由于内存块只是改变了所有权,它在 emplace_back.

之后仍然有效

std::string::c_str() :

The pointer returned may be invalidated by further calls to other member functions that modify the object.


std::vector::emplace_back :

If a reallocation happens, all contained elements are modified.


由于无法知道在调用 emplace_back 时是否会发生 vector 重新分配,因此您必须假设随后使用来自 string::c_str() 导致未定义的行为。

由于未定义的行为是 - 未定义的 - 任何事情都可能发生。因此,您的代码可能看起来有效,也可能看起来失败。无论如何都是错误的。