【问题标题】:vector assignation on uninitialized memory chunk and similar issues未初始化内存块和类似问题的向量分配
【发布时间】:2023-03-20 10:15:01
【问题描述】:

例如vector<vector<string> > test
g++,你可以做

test.reserve(10);
test[0] = othervector;
test[9] = othervector;

它不会崩溃。理论上说你不应该那样做,因为你将一个向量分配给一个相信它是一个向量的内存块。
但它就像下一个一样工作:

#include <string>
#include <vector>
#include <iostream>
using namespace std;
int main(){
    vector<string> first_vector;
    vector<string> &second_vector = *(vector<string>*)new char[sizeof(vector<string>)];
    first_vector.push_back("whatever");
    first_vector.push_back("whatever2");
    first_vector.push_back("whatever3");
    second_vector = first_vector;
    cout << "0 " << second_vector[0] << " \n";
    cout << "1 " << second_vector[1] << " \n";
    cout << "2 " << second_vector[2] << " \n";
}

在我看来,这就像向量的赋值运算符实际上复制了向量实现的所有或至少足够的字段以使其正常工作,从而在未初始化的情况下呈现完全有效的向量。

嗯,这对我来说显然是未定义的行为,唯一的问题是它按预期工作,我在我目前正在检查的代码库中发现了很多这样的行为。

在其他容器中是否还有更多类似的情况?从来没有见过看起来那么容易犯错,但即使你犯了错误它也确实有效。

编辑:这不是关于如何正确地执行上述操作或抱怨编译器的行为,而是试图找到容易发生且以后很难发现的类似问题,例如这个。

【问题讨论】:

  • 这里也按预期工作:没有输出。 :)
  • @UncleBens,你用的是什么编译器? g++-4.1.3 按预期输出这些字符串。
  • 好吧,快讯:c++ 编译器并不能保护您免受错误的影响!这就像你开着车撞到墙上,活着出来,然后开始抱怨,因为你还没有死。 “我现在不应该死了吗?这个世界怎么了?”高兴就好,不要再这样做了。
  • 天哪!各位看帖子了吗!!!!我没有抱怨什么!!!我试图找到类似的问题来查看这个代码库!
  • 我正在使用 g++ 4.4.1 (MinGW)。我怀疑这是一种即使使用相同的编译器也可能无法获得一致结果的事情。也许分配的内存碰巧在正确的字节中包含零,这些零恰好代表一个有效的空向量/字符串? - 另外,你是说调试器也没有发现任何奇怪的东西?

标签: c++ memory memory-management vector variable-assignment


【解决方案1】:

“未定义的行为”就是这样:未定义。您在一个编译器上发现了一种情况,在一个操作系统下,行为是您所期望的。这实际上很常见 - 编译器不会将“未定义的行为”表示为“自动段错误”!但是未定义行为的问题在于,由于标准不保证它,如果您以后更改操作系统或编译器,或升级到同一编译器的更高版本,您不能依赖保持不变的行为。它很可能会在你的脸上炸开。

【讨论】:

  • 从来没有说过它应该吹。当一个未定义的行为可以被视为像这样定义时,我实际上是在询问类似的情况。保留/调整大小,显然都有效,但保留是“未定义的行为”。
  • 我确实阅读了你的整个帖子。我认为这不是一个有用的问题,因为当 UB 按预期工作时,这可能是因为编译器、操作系统、硬件、库和代码之间的特殊巧合。很难确定行为可重复的确切情况。当您调用 UB 时,还存在定义“预期行为”是什么的问题。在你原来的例子中,如果你调用test.push_back,你可能会覆盖test[0],所以resize并不完全等同于reserve
  • @Arkaitz:使用储备不是UB;使用带有无效索引的 op[] 是 UB。储备影响容量,而不是大小。调整大小会影响两者(因为它会更改大小并且容量必须> = 大小)。大小决定了哪些索引是有效的。 根据定义,UB 从未被视为定义。
【解决方案2】:

您应该使用 vector::resize 而不是 vector::reserve。

我也犯过一次这个错误,它在 32 位 Windows 上运行没有问题,但在所有内容编译为 64 位时崩溃。

这就是“未定义”的意思:它可能有效,也可能突然停止工作。不保证(甚至不保证它会崩溃)。

【讨论】:

  • 我不是问用什么,我是问类似的情况,你们看完整个帖子吗?
【解决方案3】:

所以您是在要求任意滥用 STL?

我认为一个可以让初学者怀疑库中的错误的相当典型的错误是试图在容器中存储未正确实现复制的对象。我想这可能会或可能不会起作用。

另一个常见错误是没有考虑迭代器失效(例如,在循环时插入/擦除)。

使用无效的强制转换可能会创建各种场景。

【讨论】:

  • 复制结构和正确的分配对于初学者来说是一个很好的失败点。在这里投射很明显的一点是要寻找错误。
【解决方案4】:

“其他容器中是否还有更多类似的情况?”

吨。我不是在开玩笑。我可以想出数以千计的标准。

例如,如果您传入两个不相关的迭代器,几乎所有采用 迭代器范围 的函数都会以令人讨厌的方式中断。它们也可能默默地但错误地“工作”,特别是如果您传入两个向量迭代器。

同样,在我所知道的所有实现中,对于任何类型的 T reinterpret_cast&lt;T&amp;&gt;(&amp;random_bytes).operator=(T()); 可能适用于 random_bytes 的某些值。还是 UB。

【讨论】:

  • 嗯,迭代器和强制转换对我来说也是可疑的,所以我认为它们不容易忽略错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多