32

当我clear()在 a 上使用时std::vector,它应该会破坏 中的所有元素vector,但事实并非如此。

示例代码:

vector<double> temp1(4);
cout << temp1.size() << std::endl;
temp1.clear();
cout << temp1.size() << std::endl;

temp1[2] = 343.5; // I should get segmentation fault here ....

cout << "Printing..... " << temp1[2] << endl;
cout << temp1.size() << std::endl;

现在,我应该在尝试访问已清除的向量时遇到分段错误,但它会填充那里的值(据我所知,这是非常错误的)

结果如下所示:

4
0
Printing..... 343.5
0

这是正常的吗?这是一个很难发现的错误,它基本上杀死了我的代码几个月。

4

10 回答 10

77

您无权获得分段错误。就此而言,分段错误甚至不是 C++ 的一部分。您的程序正在从向量中删除所有元素,并且您非法访问容器越界。这是未定义的行为,这意味着任何事情都可能发生。确实,发生了一些事情。

于 2013-06-24T22:35:10.887 回答
31

当您访问向量边界之外时,您会得到未定义的行为。这意味着任何事情都可能发生。任何事物。

所以你可能会得到旧值、垃圾或段错误。你不能依赖任何东西。

如果要进行边界检查,请使用at()成员函数而不是operator []. 它将抛出异常而不是调用未定义的行为。

于 2013-06-24T22:37:12.317 回答
28

来自 cppreference:

void clear();

从容器中移除所有元素。使引用包含元素的任何引用、指针或迭代器无效。可能使任何过去的迭代器无效。许多实现在调用 后不会释放分配的内存clear(),从而有效地保持向量的容量不变。

所以没有明显问题的原因是向量仍然有可用的内存。当然,这只是特定于实现的,而不是错误。此外,正如其他答案所指出的那样,您的程序也确实具有未定义的行为,可以首先访问已清除的内容,因此从技术上讲,任何事情都可能发生。

于 2013-06-24T22:35:14.677 回答
8

让我们想象一下你很富有(也许你是或者你不是......无论如何)!

既然你很富有,你就可以在茉莉雅岛(法属波利尼西亚向风群岛)购买一块土地。你很确定这是一个不错的房产,所以你在那个岛上建了一座别墅,然后住在那里。你的别墅有一个游泳池、一个网球场、一个大车库,还有更多好东西。

一段时间后,您会离开茉莉雅岛,因为您认为它变得非常无聊。运动多,但人少。你卖掉了你的土地和别墅,并决定搬到其他地方。

如果您稍后再回来,您可能会遇到很多不同的事情,但您甚至无法确定其中之一。

  • 你的别墅可能会消失,取而代之的是俱乐部酒店。
  • 你的别墅可能还在。
  • 该岛可能会沉没。
  • ...

谁知道?即使别墅可能不再属于您,您甚至可以再次跳入游泳池或打网球。旁边可能还有另一栋别墅,您可以在更大的游泳池中游泳,而不会分散您的注意力。

如果你再次回来,你无法保证你会发现什么,这与你的向量相同,它在我看过的实现中包含三个指针:(名称可能不同,但功能大多相同.)

  • begin指向分配的内存位置(即X)的开始
  • end指向分配内存的末尾+1(即开始+4)
  • last它指向容器中的最后一个元素+1(即begin+4)

通过调用 clear 容器可以很好地销毁所有元素并重置last = begin;。该函数size()很可能会return last-begin;出现,因此您会观察到容器大小为 0。尽管如此,begin它可能仍然有效并且可能仍然分配了内存(end可能仍然是begin+4)。您甚至可以观察到您在 clear() 之前设置的值。

std::vector<int> a(4);
a[2] = 12;
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;
a.clear();
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;

印刷:

上限 4,ptr 为 00746570,val 2 为 12
上限 4,ptr 为 00746570,val 2 为 12

为什么你没有观察到任何错误?这是因为std::vector<T>::operator[]不执行任何越界检查(与之相反std::vector<T>::at())。由于 C++ 不包含“段错误”,您的程序似乎可以正常运行。

注意:如果在调试模式下编译,MSVC 2012operator[]会执行边界检查。

欢迎来到未定义行为的土地!事情可能发生也可能不会发生。您甚至可能无法对单一情况有所了解。您可以冒险并足够大胆地研究它,但这可能不是生成可靠代码的方式。

于 2013-06-24T23:41:59.177 回答
6

operator[]是高效的,但要付出代价:它不执行边界检查。

有更安全有效的方式来访问向量,比如迭代器等。

如果您需要一个用于随机访问的向量(即不总是顺序的),请在编写程序时非常小心,或者使用效率较低的at(),在相同条件下会引发异常。

于 2013-06-24T23:11:52.853 回答
2

您可能会遇到段错误,但这并不确定,因为使用operator[]afterclear()调用 before 访问超出范围的向量元素只是未定义的行为。从您的帖子来看,您似乎想尝试元素是否被破坏,以便您可以at为此目的使用公共功能:

该函数自动检查 n 是否在向量中有效元素的范围内,如果不在则抛出 out_of_range 异常(即,如果 n 大于或等于其大小)。这与不检查边界的成员 operator[] 形成对比。

此外,之后clear()

与此容器相关的所有迭代器、指针和引用均无效。

http://www.cplusplus.com/reference/vector/vector/at/

于 2013-06-24T22:49:40.080 回答
2

尝试访问您用于构造函数的元素 sup 超过4可能是您会遇到分段错误 cplusplus.com 的另一个想法:

清除内容

从向量中删除所有元素(已销毁),使容器大小为 0。

不保证会发生重新分配,也不保证向量容量会由于调用此函数而改变。强制重新分配的典型替代方法是使用交换:

向量().swap(x); // 清除 x 重新分配

于 2013-06-25T08:33:45.507 回答
2

如果你使用

temp1.at(2) = 343.5;

代替

temp1[2] = 343.5;

你会发现问题。推荐使用 的功能at()operator[]不检查边界。您可以在不知道 STL 矢量实现的情况下避免该错误。

顺便说一句,我在我的Ubuntu (12.04)中运行你的代码,结果就像你说的那样。但是,在Win7中,它报告“断言失败”。

嗯,这让我想起了字符串流的类型。如果定义句子

stringstream str;
str << "3456";

如果 REUSE str,我被告知这样做

str.str("");
str.clear();

而不仅仅是使用句子

str.clear();

resize(0)Ubuntu中试过了,结果没用。

于 2013-06-25T05:54:44.030 回答
1

是的,这很正常。clear()不保证重新分配。尝试使用resize()之后clear()

于 2013-06-24T23:17:48.187 回答
0

到目前为止,对答案的一个重要补充:如果实例化向量的类提供了一个析构函数,它将在清除时调用(以及在 上resize(0))。

尝试这个:

struct C
{
    char* data;
    C()           { data = strdup("hello"); }
    C(C const& c) { data = strdup(c.data); }
    ~C()          { delete data; data = 0; };
};
int main(int argc, char** argv)
{
    std::vector<C> v;
    v.push_back(C());
    puts(v[0].data);
    v.clear();
    char* data = v[0].data; // likely to survive
    puts(data);             // likely to crash
    return 0;
}

该程序很可能会因分段错误而崩溃-但(很可能)不是在char* data = v[0].data;,而是在行puts(data);(使用调试器查看)。

典型的向量实现使分配的内存保持不变,并在调用析构函数后保持原样(但是,不能保证 - 请记住,这是未定义的行为!)。最后要做的就是将 C 实例的数据设置为 nullptr,虽然对于 C++/vector 而言无效,但内存仍然存在,因此可以(非法)访问它而不会出现分段错误。char* data当在puts中取消引用指针时会发生这种情况,因为它是空的......

于 2016-07-21T08:03:01.107 回答