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

Sad*_*ido 6 c++ debugging

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

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

让我开始列表:

  1. 使用未初始化的变量.常见的印刷错误,如mMember = mMember;
  2. 线程同步.有时它可能是运气的问题;
  3. 使用非智能指针,取消引用无效指针;

还有什么?

sbi*_*sbi 22

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

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

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

编辑以解决评论中的讨论:

我没有写过"C风格的字符串处理不是更高效".(当然,有很多在这句话否定的,但因为我觉得误读,我尽量做到精确.)我说的是高层次的结构不是一般的少高性能:std::vector不是一般的慢于人工手动操作的动态分配的C数组,因为它是一个动态分配的C数组.当然,有些情况下根据特殊要求编码的内容比任何通用解决方案都要好 - 但这并不一定意味着您将不得不求助于手动内存管理.这就是我写这个的原因,如果这些事情是必要的,那么只能在类中进行良好的封装.

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

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

  • @Zan Lynx:是的,在某些情况下,你可以通过使用字符串代替char*获得巨大的加速.比方说,当你需要字符串的大小时.常量时间.size()击败线性时间strlen().当然,你可以通过修复低效的代码来获得10倍的加速.它不是神奇有效的char*,它是字符串的向量,是构造数据的一种愚蠢的方式. (2认同)

小智 5

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

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

  • 我不能同意.那些程序员必须只支持他们自己(完美设计的)代码,这是有福的.大多数程序员也必须支持遗留(坏的,未记录的)代码. (21认同)
  • 我在过去15年中所使用的大部分遗留代码都是大量多线程的中间件.在那种环境中,调试器比无用更糟糕 - 它改变了代码的时间并实际隐藏了错误. (2认同)
  • @Adrian.决不.对于我已经完成的大多数项目来说,这是不可能的.这是其他合理的史蒂夫马奎尔颁布的"最佳实践"之一,我从未相信过. (2认同)

Mat*_*lin 5

比赛条件.

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

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