C ++中不稳定错误的最常见原因是什么?

时间:2009-08-28 12:02:37

标签: c++ debugging

我目前正在开展一个大型项目,我大部分时间都在调试。虽然调试是一个正常的过程,但是存在不稳定的错误,这些错误对于开发人员来说是最大的痛苦。该程序不起作用,有时候......有时它会这样做,而且你无能为力。

可以对这些错误做些什么?最常见的调试工具(交互式调试器,手表,日志消息)可能会导致您无处可去,因为该错误将消失...只是稍后再次出现。这就是为什么我要求一些启发式方法:这些错误最常见的原因是什么?我们应该调查哪些可疑代码来找到这样的错误?

让我开始列表:

  1. 使用未初始化的变量。 常见的印刷错误如mMember = mMember;
  2. 线程同步。 有时它可能是一个问题 运气;
  3. 使用非智能 指针,解除引用无效 那些;
  4. 还有什么?

9 个答案:

答案 0 :(得分:22)

IME许多项目的根本问题是开发人员使用C ++的低级功能,如手动内存管理,C风格的字符串处理等,即使它们很少需要(然后只能很好地封装在类中) 。这会导致内存损坏,指针无效,缓冲区溢出,资源泄漏等等。一直都是漂亮而干净的高级构造。

我是一个大型(几个MLoC)应用程序团队的成员多年,应用程序不同部分的崩溃错误数量与这些部分中使用的编程风格很好地相关。当被问到为什么他们不会改变他们的编程风格时,一些罪魁祸首回答说他们的风格通常会产生更多的表现。 (这不仅是错误的,而且客户宁可拥有一个更稳定但速度更慢的程序而不是快速崩溃的程序。此外,他们的大多数代码甚至不需要快速...)< / p>

至于多线程:我觉得在这里提供解决方案并不够专业,但我认为Herb Sutter's Effective Concurrency columns是非常值得一读的。

编辑以解决评论中的讨论

我没有写过“C风格的字符串处理不是更高效”。 (当然在这句话中有很多否定,但是因为我感到误读,所以我试着准确。)我所说的是高级构造通常不太高效:std::vector不是 in一般比手动执行动态分配的C数组慢,因为它是一个动态分配的C数组。当然,有些情况下根据特殊要求编码的内容比任何通用解决方案都要好 - 但这并不一定意味着您将不得不求助于手动内存管理。这就是我写这个的原因,如果这些事情是必要的,那么只能在类中进行良好的封装。

但更重要的是:在大多数代码中,差异并不重要。一个按钮在有人点击它之后压缩0.01秒或者0.05secs只是无关紧要,所以即使是因子5速度增益也与按钮的代码无关。但是,代码是否崩溃,总是很重要。

总结我的论点:首先让它正常运作。最好使用久经考验的现成构建模块。然后测量。然后使用久经考验的现成成语提高性能。

答案 1 :(得分:5)

我实际上要发一个问题正好相反的问题 - 其他人是否像我一样发现在使用C ++时几乎没有时间使用调试器?老实说,我记不清上次使用它了 - 它一定是六个月前的。

坦率地说,如果你把大部分时间花在调试器上,我认为你的基本编码实践存在一些问题。

答案 2 :(得分:5)

比赛条件。

这些是在调试(或在问题跟踪器)中出现时仍然让我感到颤抖的少数事情之一。调试固有的可怕,并且非常容易创建。我的C ++软件中三个最常见的错误原因是竞争条件,对未初始化内存的依赖以及对静态构造函数顺序的依赖。

如果你不知道竞争条件是什么,那么它们可能是你不稳定的原因;)

答案 3 :(得分:3)

如果你真的处于一个已经有坏代码的位置,那么最好的计划可能是尽可能多地使用它(OS / lib级内存检查,自动化测试,日志记录,核心转储)等)找到问题领域。然后重写代码以做一些更确定的事情。大多数错误来自人们大部分时间都在工作的事情,但如果你使用正确的工具和方法,C ++会提供更强的保证。

答案 4 :(得分:2)

还没有看到提到的这个:

  • 继承自没有虚析构函数的类。

答案 5 :(得分:1)

  • 在通过内存写回缓存行时从未缓存的内存中读取(这是一个可以找到的正确的混蛋)。
  • 缓冲区覆盖
  • 堆栈溢出!

我能想到的只有3个...可以稍后编辑:)

答案 6 :(得分:1)

  • 缓冲区溢出
  • 使用指向已删除对象的指针
  • 返回无效的引用或引用超出范围对象
  • 未处理的例外
  • 资源泄漏(不仅是内存)
  • 无限递归

  • 动态库版本不匹配

答案 7 :(得分:1)

不是C ++问题,而是在C / C ++项目中看到的。

我必须处理的最棘手的问题是在我们的平台上启动操作系统时导致异常崩溃时的初始化问题。我们花了几年才发现发生了什么。在此之前,我们一夜之间运行系统,如果它没有崩溃,那么它通常是正常的。 幸运的是,操作系统不再销售。

答案 8 :(得分:0)

分配之前或解除分配之后使用的地址和内存,分段错误,数组跳出,偏移,线程锁定,无法理解的运算符重载,内联汇编,void退出和一般情况下需要返回值的void会使math.h函数值得一看因为所有math.h函数都具有工作参数和返回值,与其他库相比过于空洞,空白测试,空,空和空隙。我推荐的4个一般约定是返回值,参数,三元选择和可逆变化。要避免的错误是向量(使用数组而不是)使用空参数进行void,并且在我的主观意见中,我避免使用switch语句来支持更易理解或可读的if ifif或更抽象的“is”。

与脚本和解释相比,C ++也具有相当糟糕的前向兼容性,为了尝试十年前的Java,它在以后的vm中仍然保持不变且安全。

相关问题