字符串到矢量的转换抛出 std::bad_alloc

string to vector conversion throws std::bad_alloc

本文关键字:std bad alloc 转换 字符串      更新时间:2023-10-16

我在从字符串创建向量时遇到问题。有人可以解释一下下面 4 种情况之间的区别吗?根据 cplusplus.com,出于同样奇怪的原因,我希望Case 1Case 2一样工作,但事实并非如此。

typedef std::vector<uint8_t> uint8vec_t;
std::string keySecret ()
{
    return std::string ("SomeSecret");
}

案例1

// throws std::bad_alloc
uint8vec_t vSecret (keySecret ().begin (), keySecret ().end ());

案例2

// results in a vector with strange length
uint8vec_t vSecret (keySecret ().begin (), keySecret ().begin () + keySecret ().length ());

案例3

// throws std::bad_alloc
uint8vec_t vSecret (&keySecret ()[0], &keySecret ()[keySecret ().length ()]);

案例4

// throws std::bad_alloc
uint8vec_t vSecret (&keySecret ()[0], &keySecret ()[0] + keySecret ().length ());

它们都很糟糕,你只是碰巧在第二种情况下得到了看起来像正确的行为。问题是keySecret每次都返回不同的std::string对象。您不能对一个调用begin,对另一个调用end,并期望它们以任何方式相关。

相反,您应该调用keySecret一次,创建本地副本,然后在该单个本地对象上调用beginend

在两次调用 keySecret() 之后,内存中有两个不同的字符串:

...x..x....SomeSecret..x.x.x...x..x..x.x...SomeSecret....x.x..x.x..
           ^         ^                       ^         ^
           begin1    end1                    begin2    end2

然后,您尝试使用任一方法创建向量

vector<char> v(begin1, end2);

vector<char> v(begin2, end1);

(取决于哪个字符串恰好在内存中较早,哪个字符串由哪个keySecret()调用返回)。

第一个将分配end2 - begin1字节并将"SomeSecret..x.x.x...x..x..x.x...SomeSecret"复制到该内存中,两个迭代器之间具有任意数量的字节,包含字符串之间恰好在内存中的任何内容。这是未定义的行为,因为读取超过了为第一个字符串分配的内存的末尾。

第二个将尝试查找负数的距离end1 - begin2,因此换行到一个非常大的无符号数字,并尝试分配这么多字节失败,引发bad_alloc异常。

在这两种情况下,对指向不同对象的两个不相关的指针执行指针算术都是未定义的行为,因此理论上任何事情都可能发生。在实践中,结果可以相当合乎逻辑地解释,如上所述(尽管对于第一种情况来说,段错误并不是一个令人惊讶的结果,而不是内容错误的向量)。