有和没有初始化的std :: vector分段错误

时间:2014-08-17 09:41:05

标签: c++ stdvector

我正在用C ++中的矢量进行一些实验。我使用的代码如下

#include <vector>
#include <iostream>

int main()
{
   std::vector<float> aVector = std::vector<float>(10);  // IMPORTANT LINE
   std::cout << "Vector size: " << aVector.size() << std::endl;
   aVector.clear();
   aVector[0] = 2.2;
   std::cout << "Vector size: " << aVector.size() << std::endl;
   aVector.push_back(0.1);
   std::cout << "Vector size: " << aVector.size() << std::endl;
   aVector.clear();
   std::cout << "Vector size: " << aVector.size() << std::endl;
   aVector[0] = 2.1;
   std::cout << "Vector size: " << aVector.size() << std::endl;

   return 0;
}

如果我有向量,用N个元素初始化它,然后清除向量并尝试将赋值aVector[0] = 1.1放到程序一直到最后没有分段错误。

另一方面,如果我创建了一个向量但没有传递初始元素的数量,那么在尝试使用aVector[0] = 1.1分配值时,它会使程序崩溃。

出现分段错误的唯一方法是因为我试图写入无效的内存地址。因此,根据我的理解,创建向量而不是初始化意味着指向实际向量数据的指针指向无处,而初始化向量和清除时保持指向已分配内存的指针,但向量的大小仅缩小为0?是否意味着即使在清除了矢量之后,先前存储的值仍然存在?

这是规范中定义的行为吗?我希望清除擦除并释放以前分配的内存。

2 个答案:

答案 0 :(得分:7)

向量分配的内存不限于向量使用的内存。让我们一步一步地完成该计划。请注意,我将描述实际发生的内容;正式地,当您aVector[0] aVector.size()==0时,无论您最初是否分配了任何元素,都会调用未定义的行为:

std::vector<float> aVector = std::vector<float>(10);  // IMPORTANT LINE

这里初始化向量,默认初始化10个元素。也就是说,它为10个浮点数分配空间,并将每个浮点数初始化为零。内存现在看起来像这样:

+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
    |      |
    |      \----------------------------------------------------\
    |                                                           |
    V                                                           V
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

容量告诉您已分配了多少个单元格。

请注意,你可以写一下std::vector<float> aVector(10);

现在你做

std::cout << "Vector size: " << aVector.size() << std::endl;

函数std::vector<>::size()只计算beginend之间的差异。因此,对于10begin之间的10个元素,您获得end

aVector.clear();

这会将end重置为等于begin并销毁超出该新端的所有对象(float通常意味着什么都不做;但是调试版本可能会例如用NaN覆盖浮点数) 。也就是说,你的矢量现在看起来像这样:

+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
    |      |
    |/-----/
    |
    V
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

你看,记忆仍在那里。

aVector[0] = 2.2;

由于std::vector<>::operator[]不进行范围检查(尽管调试版本可能会执行范围检查),因此会导致begin指向的float的赋值。因为即使当前没有初始化浮点数也可以分配(如果你有例如std::vector<std::string>,情况会有所不同),这个赋值成功了,你现在有了

+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
    |      |
    |/-----/
    |
    V
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    | 2.2 | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

接下来,

std::cout << "Vector size: " << aVector.size() << std::endl;

当然会给0,因为这是beginend之间的区别。

aVector.push_back(0.1);

成员push_back将首先检查end之后是否存在其他元素的空间(在这种情况下显然是这样),如果不存在则重新分配(现在不适用)然后简单地构造一个新对象在*end和增量end的值。所以现在你的矢量看起来像这样:

+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
    |      |
    |     //
    |     |
    V     V
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    | 0.1 | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

正如您所看到的,对于向量,2.2实际上不存在,因此它被新构造的0.1覆盖。

由于程序的其余部分只是重复前面的步骤(结果相同),所以我就此止步。

现在,如果在开始时你没有请求任何元素,则向量最初不会分配内存,因此尝试写入aVector[0]将导致分段错误。

答案 1 :(得分:0)

the doc开始,clear()使std::vector对象的任何元素的所有内容(引用,指针...)无效,但由于capacity()仍然存在,因此不会释放内存不变。