std::vector<> 在工作当中的使用频率比较高,因为它简单易用,类似数组,但又比数组智能得多,它能动态调整内存以容纳足够多的数据。它和数组相似的一点,用下标进行元素的访问。这里就有一个不易察觉的陷阱,假如是数组我们是可以通过 array[n] = xx (这里n是有效范围的) 进行赋值,但在 std::vector<> 里,这个是获取元素用的,它返回的是元素的引用。所以当 std::vector<> 变量为初始状态空时,这个操作会导致segmentation fault 的。代码如:
- #include
- #include
- #include
-
- void vecPint(std::vector<int> &vec);
-
- int main()
- {
- std::vector<int> intVec;
- intVec[0] = 30;
- vecPint(intVec);
-
- return 0;
- }
-
- void vecPint(std::vector<int> &vec)
- {
- for(size_t i = 0; i < vec.size(); i++)
- {
- printf("intVec[%d] = %d\n", i, vec[i]);
- }
- }
std::vector
intVec[0] = 30;
定义 vector 后直接用 [] 进行赋值是非法的,因为此时 vector 内部的指针是空的,访问空指针是会引起 segmentation fault。可以通过 GDB 查看内部数据信息:

在第 10 行处打了一个断点,即此行:intVec[0] = 30; 打印变量 intVec 信息,我们看到此时 intVec 元素个数为0,分配的储存空间也是0。然后 单步 到 operator[] 里,其返回是这个:return *(this->_M_impl._M_start + __n),而此时 this->_M_impl 还是空的:

所以操作一个空指针,必引起 segmentation fault 的。那如果是这种情况呢:
- #include
- #include
- #include
-
- void vecPint(std::vector<int> &vec);
-
- int main()
- {
- std::vector<int> intVec;
- intVec.push_back(30);
- intVec[1] = 20;
- vecPint(intVec);
-
- return 0;
- }
-
- void vecPint(std::vector<int> &vec)
- {
- for(size_t i = 0; i < vec.size(); i++)
- {
- printf("intVec[%d] = %d\n", i, vec[i]);
- }
- }
先 push_back() 再 [],因为 push_back() 会引起内存分配。这样是不会出现段错误的,但实际运行结果是这样的:

只输出了一个元素。怎么会是这样的呢?从 GDB 上看:

在 intVec[1] = 20 处打断点。单步到 operator[] 函数里,此时只有一个元素(即:_M_finish - _M_start = 4,即 4 byte),存储空间也只能容纳一个元素(即:_M_end_of_storage - _M_start = 4,即 4 byte),而当执行过 intVec[1] = 20 后,前后内存数据对比:

20 已经被存储到 intVec 所占用内存的后面,16进制打印即为:0x00000014。因为我们打印时只会输出 intVec 内存范围内的数据,所以 20 是没有被输出的。至于能够存储到 intVec 内存之后不会出现问题,我想应该是:只要是可以访问到的内存地址,应该是没问题的。如果有大神更了解,希望指正。既然这个 20 已经存储到了 intVec 内存之后,那我们也是可以打印出来的,如:
- void vecPint(std::vector<int> &vec)
- {
- for(size_t i = 0; i < vec.size() + 1; i++)
- {
- printf("intVec[%d] = %d\n", i, vec[i]);
- }
- }
我们把循环加大一个元素,这样不就跳到最后元素的后面了吗?其结果如下:

20 这个值也打印出来了。
总结:下标操作慎用,因为下标有可能超出范围而抛出异常,且下标操作需要在变量非空的情况下使用。下标操作期望是“修改”而非“赋值”。