• std::vector 的使用陷阱


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

    1. #include
    2. #include
    3. #include
    4. void vecPint(std::vector<int> &vec);
    5. int main()
    6. {
    7. std::vector<int> intVec;
    8. intVec[0] = 30;
    9. vecPint(intVec);
    10. return 0;
    11. }
    12. void vecPint(std::vector<int> &vec)
    13. {
    14. for(size_t i = 0; i < vec.size(); i++)
    15. {
    16. printf("intVec[%d] = %d\n", i, vec[i]);
    17. }
    18. }

        std::vector 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 的。那如果是这种情况呢:

    1. #include
    2. #include
    3. #include
    4. void vecPint(std::vector<int> &vec);
    5. int main()
    6. {
    7. std::vector<int> intVec;
    8. intVec.push_back(30);
    9. intVec[1] = 20;
    10. vecPint(intVec);
    11. return 0;
    12. }
    13. void vecPint(std::vector<int> &vec)
    14. {
    15. for(size_t i = 0; i < vec.size(); i++)
    16. {
    17. printf("intVec[%d] = %d\n", i, vec[i]);
    18. }
    19. }

    先 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 内存之后,那我们也是可以打印出来的,如:

    1. void vecPint(std::vector<int> &vec)
    2. {
    3. for(size_t i = 0; i < vec.size() + 1; i++)
    4. {
    5. printf("intVec[%d] = %d\n", i, vec[i]);
    6. }
    7. }

    我们把循环加大一个元素,这样不就跳到最后元素的后面了吗?其结果如下:

    20 这个值也打印出来了。 

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

  • 相关阅读:
    微信小程序数据绑定和条件渲染
    受控组件——受控组件简写——受控表单组件-——复选框取消全选等
    leetcode-621. 任务调度器
    Grafana-web使用说明
    Maven常用命令、坐标、依赖管理、依赖范围
    L2-030 冰岛人(Python3)
    git 学习总结
    stream final 生成序号
    python 脚本 将一个文件夹下的所有文件遍历替换某些内容(将简体变为繁体)
    Kubernetes 1.25 集群搭建
  • 原文地址:https://blog.csdn.net/tianyexing2008/article/details/126359192