当前位置: 首页 > news >正文

C++缺陷识别于调试

一、C++缺陷概述

  • C++语言编写的程序的缺陷大部分继承于C语言本身的语法使用缺陷,同时有一部分也来源于C++本身语法的缺陷。(但我理解规则是固定的,缺陷来源于你如何使用,而非设计)

  • 尽量让缺陷暴露在编译阶段,通过不断完善的编译器来发现缺陷。

    • 避免使用编译器的隐式类型转换,建议使用explicit修饰带一个入参的构造函数,并避免使用转换赋值运算符号

    • 使用类型作为入参,在某些场景下尽量不要将enum当作整型数使用

    • 定义合适的类来表示特定场景下的数据类型,而不是使用double类型,但指挥使用到0~10000范围的数

  • 大部分的缺陷仍会暴露在运行阶段,如数组或指针越界访问

    • 运行时错误分为两种:程序本身的缺陷 + 外部因素导致的错误(如输入错误、无网络、无权限、文件不存在、硬件缺失等)

    • 通过编写安全检查相关的代码来自动检查程序本身的缺陷,安全检查捕获到缺陷时,可以做

      • 记录错误信息及变量值

      • 做适当的处理,如直接退出函数或者配出异常

    • 安全检查会增加工作量,同时降低执行效率,可通过宏开关控制启用阶段

二、具体C++常见缺陷

1. 索引越界

  • 数组分为静态数据、动态数据和vector,数组元素均可通过一个无符号整数作为索引进行访问

    T StaticArr[20];
    T* pDynamicArr = new T[20];
    std::vector<T> vecArr;
    • 动态数组: std::vector.at(index)函数会进行out_of_range的越界访问检查, 但若大量使用,代码效率会降低

    • 建议还是通过[]进行数组元素的访问,但可新定义一个继承于std::vector的vector类型,重写[]操作符函数进行安全检查,并通过宏定义开关

    • 静态数组可通过定义array模板类,重写[]操作符,加入安全检查代码

    • 多维数据通过定义matrix模板类,重写[]操作符,加入安全检查代码

  • 不要使用静态或动态数组,可以改用自己实现的array和vector模板类

  • 避免使用new T[]分配动态数组内存,建议使用vector模板为多个元素分配内存

  • 使用自定义的vector代替std::vector,使用自定义的ayyay代替静态数组,并视场景打开安全检查

  • 对于多维数据,使用自定义的matrix,并通过()操作符访问元素,并视场景打开安全检查

2. 指针运算

  • 指针允许被修改它本身的值以及指向的值

  • 指针运算是数组索引访问内存的另一种语法

  • 建议避免使用指针运算,而使用vector模板或者数组索引,因为他们可以重载[],加入安全检查

3. 无效的指针、引用和迭代器

当向vector中插入新的元素时,会重新分配空间并拷贝原来的元素,这将导致原来的元素的地址发生变化,如以下案例

vector<int> vecVal;
for(int i = 0; i < 10; ++i)
{vecVal.push_back(i);
}
int* pArr3 = &vecVal[3];
int& Arr3 = vecVal[3];
vector<int>::const_iterator begin = vecVal.begin()
cout << "pArr3 val = " << (*pArr3) << ", pArr3 address = " << pArrs << endl;
​
// 插入新的元素
for(int i = 0; i < 100; ++i)
{vecVal.push_back(i*10);
}
cout << "vecVal[3] val = " << (*pArr3) << ", pArr3 address = " <<&vecVal[3]; << endl;

  • vec[3]输出的值不会变化,地址发生了变化

  • pArr3向的地址应该被回收,可能会被其他变量分配,此时若写入值,可能会引起错误

  • 用迭代器指向vector中的值,在重新分配空间后,其指向也会发生变化


  • 建议在修改vector之前得到的指向其中元素的指针、引用、迭代器在vector由于增加元素而被修改之后就不应该再使用了;

  • 其他STL容器或者即将纳入标准的容器类如hash_map、hash_set在被修改(新增元素或删除元素后),其之前的指针、索引,迭代器也不建议使用.

  • 当容器在跨线程使用时,特别要注意容器内容的变化与指针、引用、迭代器使用时的时机

  • vector使用[]索引访问时,会计算出当前的新地址,其他容器则必须先获取新容器的元素新地址再使用。

  • 再修改了容器之后,务必不要再保存和使用之前指向容器的指针、引用和迭代器.

4. 未初始化的变量

  • 内置变量类型没有构造函数,在没有初始化的时候是随机值

  • 可通过构建模板类型,将内置类型(int、float等)封装使用,提供构造函数,避免未初始化的场景,可见源码(https://github.com/vladimir-kushnir)中Tnumber<T>的案例

  • 内置类型bool未赋初始值时同样可使用封装类型class Bool来提供构造函数

针对内置类型变量忘记初始化的问题,建议

  • 不要直接只用int,unsigned,double,bool等内置类型作为类的成员,反之,可使用Int,Unsigned,Double,Bool等封装类型,可避免忘记初始化导致赋值随机值

  • 在形参传递时多了一层安全检查;

5. 内存泄露

内存泄露是指在堆上分配了一块内存,并把这块内存的地址赋值给了一个指针,结果这个指针离开了作用域或者指向了另一块堆内存,忘记释放原来那块堆内存,导致原来的那块内存无人可知也无法回收使用的情况。

A和B两个对象分别包含了一个指向对方的指针,此种情况下,释放任何一个对象,另一个对象的内存就会泄露——循环引用

----------------------------------------------------未完待续---------------------------------------------------------------

源码来源:https://github.com/vladimir-kushnir

参考书籍:《C++编程调试秘笈》


http://www.mrgr.cn/news/62968.html

相关文章:

  • Linux 文件内容显示
  • react hook
  • Web项目实战(附视频+文档)
  • Redis特性和应用场景以及安装
  • 硬盘的管理
  • 如何使用AdsPower指纹浏览器克服爬虫技术限制,安全高效进行爬虫!
  • STM32的USB接口介绍
  • 使用GitLab CI/CD流水线自动化软件交付
  • leetcode 704 二分查找
  • .[support2022@cock.li].colony96勒索病毒数据怎么处理|数据解密恢复
  • 篡改猴 (Tampermonkey) 安装与使用
  • 【编程知识】C语言/c++的cast是什么
  • GitHub Spark:GitHub 推出零代码开发应用的 AI 编程产品
  • .net framework 3.5sp1开启错误进度条不动如何解决
  • Vue生命周期
  • WPF+MVVM案例实战(十五)- 实现一个下拉式菜单(上)
  • ubuntu基于docker-compose部署mysql5.7和mysql8.0
  • [ 问题解决篇 ] 新装虚拟机 Windows server 2012 无法 ping 通(关闭/开启防火墙详解)
  • C++抽象类(接口)
  • 2024网鼎杯初赛-青龙组-WP
  • AI大模型原来这么简单!一篇文章拯救你的技术恐惧症
  • liunx网络套接字 | 实现基于tcp协议的echo服务
  • 来了解一下!!!——React
  • CF979
  • 函数的调用
  • JS中DOM和BOM