std::vector 的使用陷阱

14 篇文章 0 订阅

        std::vector<> 在工作当中的使用频率比较高,因为它简单易用,类似数组,但又比数组智能得多,它能动态调整内存以容纳足够多的数据。它和数组相似的一点,用下标进行元素的访问。这里就有一个不易察觉的陷阱,假如是数组我们是可以通过 array[n] = xx (这里n是有效范围的) 进行赋值,但在 std::vector<> 里,这个是获取元素用的,它返回的是元素的引用。所以当 std::vector<> 变量为初始状态空时,这个操作会导致segmentation fault 的。代码如:

#include <vector>
#include <stdio.h>
#include <stdlib.h>

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<int> intVec;

    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 <vector>
#include <stdio.h>
#include <stdlib.h>

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 这个值也打印出来了。 

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

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值