当我在clear()
上使用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
这是正常的吗?这是一个非常难以发现的错误,基本上几个月来我的代码被杀了。
答案 0 :(得分:76)
您无权获得细分错误。就此而言,分段错误甚至不是C ++的一部分。你的程序 从向量中删除所有元素,并且你非法访问容器越界。这是未定义的行为,这意味着任何事情都可能发生。确实发生了一些事情。
答案 1 :(得分:28)
当您在向量的边界之外访问时,您将获得未定义的行为。这意味着一切都会发生。任何东西。
所以你可以获得旧的值,垃圾或seg-fault。你不能依赖任何东西。
如果要进行边界检查,请使用at()
成员函数而不是operator []
。它将抛出异常而不是调用未定义的行为。
答案 2 :(得分:26)
来自cppreference:
void clear();
从容器中删除所有元素。使引用包含元素的任何引用,指针或迭代器无效。可能使任何过去的迭代器无效。 许多实现在调用
clear()
后不会释放已分配的内存,从而有效地保持向量的容量不变。
因此没有明显问题的原因是因为向量仍然具有存储中可用的内存。当然,这只是特定于实现的,但不是错误。此外,正如其他答案所指出的那样,您的程序也确实具有未定义的行为以便首先访问已清除的内容,因此技术上任何事情都可能发生。
答案 3 :(得分:7)
让我们想象你是富有的(也许你是或者你不是......无论如何)!
由于你很富有,你在Moorea(法属波利尼西亚向风群岛)购买了一片土地。 你非常肯定这是一个不错的财产,所以你在那个岛上建造一个别墅,你住在那里。 你的别墅有游泳池,网球场,大车库和更好的东西。
过了一段时间,你离开了Moorea,因为你认为它变得非常无聊。很多运动但人很少。 你出售你的土地和别墅,并决定搬到其他地方。
如果你以后再回来,你可能会遇到很多不同的事情,但你甚至不能确定其中一个。
如果你再次回来,你无法保证发现你会发现什么,而且你的矢量包含了我所看到的实现中的三个指针: (名称可能不同,但功能大致相同。)
begin
指向分配的内存位置的开头(即X)end
指向分配的内存+1的结尾(即begin + 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 2012上operator[]
执行边界检查,如果在调试模式下编译。
欢迎来到未定义行为的土地!事情可能会也可能不会发生。你可能甚至无法对单一情况采取任何措施。 您可以冒险并大胆地查看它,但这可能不是生成可靠代码的方法。
答案 4 :(得分:6)
operator[]
效率很高,但需要付出代价:它不会执行边界检查。
访问矢量有更安全有效的方法,比如迭代器等。
如果你需要一个随机访问的向量(即并不总是顺序),要么对你编写程序的方式要非常小心,要么使用效率较低的at()
,它在相同的条件下会抛出异常
答案 5 :(得分:2)
你可以得到seg错误,但这不是肯定的,因为在operator[]
调用之前访问带有clear()
的向量的范围元素只是未定义的行为。从您的帖子看起来您想要尝试元素是否被销毁,以便您可以使用at
公共函数来实现此目的:
该函数自动检查n是否在范围内 向量中的有效元素,如果它,则抛出out_of_range异常 不是(即,如果n大于或等于其大小)。这是在 与成员运算符[]形成对比,不检查边界。
此外,在clear()
之后:
与此容器相关的所有迭代器,指针和引用都是 无效。
答案 6 :(得分:2)
如果您使用
temp1.at(2) = 343.5;
而不是
temp1[2] = 343.5;
你会发现问题所在。建议使用at()
的功能,而operator[]
不检查边界。您可以在不知道STL向量的实现的情况下避免该错误。
顺便说一下,我在 Ubuntu(12.04)中运行你的代码,结果就像你说的那样。但是,在 Win7 中,报告“断言失败”。
嗯,这让我想起了stringstream的类型。如果定义句子
stringstream str;
str << "3456";
如果重播str
,我被告知要这样做
str.str("");
str.clear();
而不只是使用句子
str.clear();
我尝试了 Ubuntu 中的resize(0)
,结果证明没用。
答案 7 :(得分:2)
尝试访问用于构造函数的 4 元素可能会导致您的分段错误 来自cplusplus.com的另一个想法:
清除内容
从向量中删除所有元素(被销毁),使容器的大小为0。
无法保证重新分配,并且由于调用此函数,无法保证向量容量发生变化。强制重新分配的典型替代方法是使用swap:
<强>矢量()交换(X)。 //清除x重新分配
答案 8 :(得分:1)
是的,这是正常的。 clear()
不保证重新分配。在resize()
之后尝试使用clear()
。
答案 9 :(得分: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中无效,但内存仍然存在,因此可以非法访问它(没有分段错误)。在puts中解除引用char* data
指针时会发生这种情况,因为它是null ...