在我的职业生涯中,我注意到一些开发人员不使用调试工具,而是对错误代码进行抽查以找出问题所在。

虽然很多时候无需调试器就可以快速发现代码中的错误是一项很好的技能,但是似乎花费大量时间查找问题的效率不高,而调试器很容易发现一些问题像错字之类的错误。

是否可以在没有调试器的情况下管理复杂系统?这是明智的吗?使用“心理调试”有什么好处?

评论

乔纳森,您好,我已对您的问题进行了修订,以避免陷入困境,并保持开放状态:我认为-如现在所说,这是一个足够体面,可回答的问题。
示例:考虑代码a = 6/3。,而不是键入错误,您键入a = 6/2.。现在,您在助记符级别。,ADD,JMP指令中进行搜索,然后发现有一个额外的迭代而不是2.,则您意识到分隔线的错字。现在您可以推断出始终使用调试器是多么荒谬。

#1 楼

从外部猜测似乎经常被我称为“调试您的思想”。在某种程度上,这类似于大师无需看棋盘就能下棋的能力。

这是迄今为止我所知道的最有效的调试技术,因为它根本不需要调试器。 。您的大脑可以同时探索多个代码路径,从而产生比调试器可能更好的周转时间。

在进入竞争性编程领域之前,我并没有意识到这种技术,在这种情况下,使用调试器意味着损失宝贵的时间。经过大约一年的竞争,我几乎完全开始将这种技术用作我的第一道防线,然后进行调试日志记录,并使用位于遥远第三位的实际调试器。这种做法的一个有用的副作用是我开始以较慢的速度添加新的错误,因为在编写新代码时“调试中的想法”并没有停止。

当然,这种方法有其局限性,这主要是由于人们在可视化代码的多个路径方面的局限性。我学会了尊重自己的这些限制,转向调试器来修复更高级算法中的错误。

评论


+1我发现“通过猜测编程”是一个加载的短语。思维无可替代。 OP没有解释的是“猜测”的有效性。我的怀疑是,这纯粹是猜测(即墙上的意大利面条),而是使用演绎推理。调试器占了上风,但它们并不是演绎推理和简单理解代码的灵丹妙药。

– Bill
2012年1月23日在18:06



@DJClayworth这并不完全准确:有时即使尝试使用一个好的调试器,尝试使用调试器也是一个糟糕的选择:您最终会浪费大量时间而没有完成很多工作。我立即想到的一个案例是解决并发问题。其他是调试具有高分支因子的递归算法,一些动态编程算法以及硬件中断服务程序。当然,当您确实需要调试器时不使用调试器是很愚蠢的,但是决定何时开始需要调试器是一个高度个人的选择。

–谢尔盖·卡里尼琴科(Sergey Kalinichenko)
2012年1月23日在20:23

+1尽管我发现调试器对于某些类型的错误(尤其是在更复杂的算法中)无价之宝,但对于简单地了解代码确实没有任何替代

–克里斯·布朗(Chris Browne)
2012年1月23日20:44

@DJClayworth我故意提出一个比“在某些情况下不使用调试器更好的情况”更强的声明:我与竞争性编程的短暂接触告诉我,本能地寻求调试器对我来说不是最有效的行为。这些天来,我开始于(1)快速重新读取代码,以及(2)在(3)寻求调试器之前检查调试跟踪(如果可用)。在许多情况下,第三步是不必要的,因为我在步骤(1)或(2)中发现了问题,编写了重现问题的单元测试,并编写了修复程序,所有这些都无需使用调试器。

–谢尔盖·卡里尼琴科(Sergey Kalinichenko)
2012年1月23日在20:51

我认为您真正的意思是程序员应该具有调试顺序,而不是单击魔术师的“查找错误”按钮。调试器是一种非常强大的工具,但是您不必启动电锯来修剪树篱。

– Spencer Rathbun
2012年1月23日在21:29

#2 楼

我对代码库了解得越多,就越不需要调试器(但是我仍然会检查所报告的错误,这在任何推理中都是重要的线索)。

这是理解的好工具某种复杂程度从小到中等的动态行为,但我经常发现它使我着重于细节而不是全局。不久之后,问题就出在这里:在更广泛范围内的交互中,其动态行为往往更易于使用其他工具来理解(例如,在模块边界记录输入和输出)。

#3 楼

他们可能不是不好的程序员,但他们可能是效率低下的疑难解答者。

我倾向于遵循调试的建议:查找最难以捉摸的软件和硬件问题(David Agans)的9条必不可少的规则,而这一规则正好落在“退出思考与观察”的指导下“

评论


我不同意,尽管我不会拒绝投票。正如delnan所说,如果您能理解代码的作用,发现错误的过程比逐步调试程序并尝试找出错误的时间要快。也就是说,当开发人员无法通过阅读代码识别问题时拒绝使用调试器,这是一个很大的错误。

–user4234
2012年1月23日19:14

@Mark加上错误诊断问题和插入新缺陷的额外好处。

–基思带来
2012年1月23日19:17

@Mark Ba​​nnister-我明白你在说什么。让我修改一下,如果您一直在代码中寻找问题15分钟以上,请放弃并使用调试器,不要固执。

–JohnFx
2012年1月23日19:35

我认为一个好的程序员不应该依赖调试器。一旦他的见解失败-或定期确保他的见解仍在轨道上,这不应阻止他立即使用(如果可用)。

–即将来临的风暴
2012年1月23日在23:41



@mark,除非您使用很小的代码库,否则我认为不可能理解每一行代码。我目前有95%的错误已通过您描述的方式解决,但更棘手的错误是您需要调试器的地方。

– wobbily_col
2014年3月3日在10:12

#4 楼

任何工作都需要以正确的方式使用正确的工具。如果您有调试器,请使用它查看实际情况。大多数错误是由假设引起的。

我与拒绝使用调试器的开发人员合作,因为他们了解得更多。我曾经得到的经典回应是“崩溃不是我造成的,我整日检查代码(崩溃的地方),没有错”。 (从db中读取的空值呢?)老板似乎认为这是一个很好的答复,但客户却没有。

我尽可能快地离开了那个团队。他们的目的是简化工作,将一个10分钟的简单问题变成一个整日忙碌的问题。

评论


+1“大多数错误是由假设引起的”是非常明智的话

– ZJR
2012年1月23日在16:19



我认为所有错误都是由假设引起的。 (看看我在那里做什么?= P)

– dan_waterworth
2012年1月23日在16:36

@ZJR:这就是为什么assert这么好的原因。检查您的假设。经常检查。

–赞·山猫
2012年1月24日17:19

@dan_waterworth:不正确。例如,这可能是一个错字。

–托马斯·埃丁
2012年12月6日20:17

#5 楼

关于调试实践的最佳指南是Steve McConnel的书《 Code Complete》。第23章详细介绍了调试,我将从中总结出一些要点。


理解问题很重要,并且使用调试器不能替代调试器。 >猜测是错误的调试方法。如果您的同事真的在使用猜测,而不是在思考问题,那么他们做的不好。猜测工作意味着在代码中粘贴随机打印语句,并希望找到有用的东西。
如果您的同事真的不知道如何使用调试器(而不是选择不使用调试器),那么是的,他们是无能的,只是就像一个不知道应该使用的语言语法的人。


评论


尽管我在大部分职位上都同意您的意见,但我认为不称职是不公平的。不使用调试器就可以进行开发,效率低下。有些人比其他人更了解调试器!

–ChrisFletcher
2012年1月23日在21:49

我不会随便乱扔“无能”这样的词。我知道有人完全使用打印语句进行调试,没有其他人可以做出他所做的贡献。

–迈克·邓拉维(Mike Dunlavey)
2012年1月24日15:55



@MikeDunlavey该人知道如何使用调试器并选择不使用它吗?精细。如果他们不知道,那我就坚持。

– DJClayworth
2012年1月24日下午16:52

随您所愿,可以很容易地将这个形容词应用于您。然后您将了解-这是校园的东西。

–迈克·邓拉维(Mike Dunlavey)
2012年1月24日19:42

#6 楼

很难说。如果您已经知道错误的根源(通过错误的值传递给库函数,可能是无效的SQL等),则可以通过猜测进行调试。我承认有时会在错误本身看起来很小或很明显的时候这样做,例如“字符缓冲区太小”-堆栈跟踪向我显示了它失败的行,并且我不需要调试器来解决这个问题。

一直这样做会适得其反,如果前几个“猜测”失败了,那么猜测可能是错误的问题解决策略,应该调用真正的调试器。通常,我会说绝对没有错就是说,我一直在使用工具和环境进行调试,这些工具和环境很难使调试器正常工作,或者因为调试器太少和没有用,所以不幸的是猜测通常是一种更好的方法。我使用过一些甚至没有适当的调试器的专有工具。我想如果一个人在这样的环境中工作太长时间,他们最终可能会失去对调试器的信任,而只能依靠猜测方法。

#7 楼

我对此主题的讨论没有提到“单元测试”感到惊讶。

因为我进行测试驱动的开发,所以我没有在调试器上花费很多时间。 10年前,我曾经认真地完成调试器的工作:


在编写了一段代码以确保它能正常工作之后,并且
当我收到错误报告以尝试诊断问题

经过10年的测试驱动开发,我发现如果满足以下条件,我将成为一名程序员,效率更高:


在编写代码以确保正确编写之前,我先编写单元测试。
在收到错误报告以尝试复制并深入研究问题后,我立即编写了单元测试。

允许计算机运行代码并验证结果的速度比我认为或逐步执行代码以从心理上验证结果的速度快数千倍,并且不会出错。
偶尔进入调试器,而我仍在进行代码的精神分析……但很少,而且主要用于非常棘手的代码。

评论


+1添加打印语句并重新运行测试然后使用调试器通常更快。

–温斯顿·埃韦特(Winston Ewert)
2012年1月24日16:14

@ winston-通常,启动调试器比编写多个打印语句要快得多,直到找到有问题的代码的位置为止。一切都取决于。简单的问题通常以您描述的方式更快地解决,但是复杂的问题是您需要调试器的地方。能够同时使用两者比严格遵守任何绝对原则要好。

– wobbily_col
2014年3月3日10:03



#8 楼

就我个人而言,我尝试通过以下方法来最大程度地减少调试器的使用:


使用静态检查器和类似的编译器选项,它们仅通过分析代码即可提示可能的错误源。具有尽可能少的副作用,尽可能多的功能,并尽可能消除可变状态
以最小的合理粒度编写单元测试
不吞并异常

当然,每个人都会出错,因此即使以这种方式编写程序,如果测试失败,我也会使用调试器检查中间表达式的值。但是,通过遵循上述原则,可以更轻松地找到缺陷,并且调试并不意味着痛苦,不确定的过程。

#9 楼

尽可能使用调试器。调试器将只是解决问题(哦,瞧,我们没有检查该值),或者提供了大量上下文,这些上下文在分析相关代码时很有用(哇,堆栈完全搞砸了,我会是缓冲区溢出的问题。)

#10 楼

调试是一种非常有用的工具,可以在运行时检查代码中对象和变量的状态。

如上面的答案中前面提到的,调试非常有用,但是在某些情况下,调试受限制。

根据我的经验,我发现使用调试器非常有用,因为它有助于揭示我对代码状态所做的错误假设。有些人并不精通代码以查找错误,因此调试可以帮助揭示您或其他开发人员对代码状态所做的错误假设。

也许您希望参数在传递给方法时永远不会为空,因此您永远不会检查这种情况并像在该参数永远不会为空的情况下那样在方法中进行。现实情况是,即使您将参数设置为永远不为null的方法的前提是,该参数最终仍会在某些时候变为null。总是会发生。

与上述示例中调试器的有用性相反,我发现当涉及多线程(即并发,异步处理)时,使用它很困难并且没有用。它可以提供帮助,但是当在A点的一个线程和B点的一个完全独立的线程中碰到调试器的断点时,容易在多线程迷雾中迷失方向。开发人员被迫按下新的断点“思维过程”,然后在新断点处将自己定向到代码。在断点B的相关性降低之后,开发人员然后切换回第一个断点,并且必须回想起在断点B触发之前他/她正在寻找的内容。我知道这可能是一个令人困惑的解释,但是我的意思是在本段中,使用并发的调试可能会增加(注意力缺陷紊乱)过程,因此保持调试思维模式的生产力可能更加困难。

并发代码的不可预测性还会进一步分散开发人员在调试并发代码方面的注意力。 >
总而言之,我的诚实观点是:


使用并发时进行调试=增加失去“调试思想模式”焦点的趋势




其他任何时间=提高调试效率b / c您的注意力不会被意外的断点(由于竞争条件而意外)打断。


评论


+1用于在并发环境中提出调试问题,在传统环境中,传统调试器的有用性通常会降低到接近于零。

–谢尔盖·卡里尼琴科(Sergey Kalinichenko)
2012年1月23日19:27

#11 楼

我认为它们有点过于刻板。
就我个人而言,当我遇到错误时,我会重新检查代码,并尝试从程序逻辑中在代码中追踪它,因为这有时可以帮助我发现其他问题或其他方面。比仅使用debbuger并修复其自身出现的错误更容易实现效果。

即使我认为我已经钉上它,也通常会对其进行调试以确保我是对的。当问题更复杂时,我相信调试是绝对必要的。

也...只是我的意见,但是,没有充分利用现代IDE可以带来的工具的借口是没有道理的。如果它可以帮助您更快,更可靠地完成工作,则应该使用它。

#12 楼

讨厌概括,但是我遇到的许多程序员都认为只有一种方法可以解决问题(他们的方法)。很容易假设已经考虑了所有可能的测试。不同的观点可能非常有价值。

通过反复试验进行编程可以提出一些很棒的新方法,并捕捉其他人所错过的东西。

缺点是,通常花费更长的时间。

#13 楼

嗯,这取决于人。就我个人而言,我自己并没有使用调试器。对微控制器进行编程时,基本上是使用LED或将数据写入EEPROM来“调试”其上的代码。我不使用JTAG。

当我为PC或服务器编程软件时,我倾向于使用日志记录和大量控制台输出。对于C风格的语言,我使用预处理程序指令,而在Java中,我使用日志级别。

由于我不使用调试器,您会说我做错了吗?这是编辑的工作,向我显示语法错误在哪里,而当逻辑上出现错误时,我只需要运行测试。

#14 楼

不需要使用调试器和不知道如何(或拒绝)使用调试器之间是有区别的。调试器只是用于跟踪和修复错误的众多工具之一。我曾与开发人员合作,他们可以使头脑风暴,而其他人则认为可以。

最好的组合是编写代码,以便通过单元测试进行测试并记录日志。错误。然后,您希望不必查看日志或使用调试器。这有点像购买保险。希望您永远不需要使用它,但是一旦遇到无法通过重新检查代码解决的错误,那么添加适当的错误处理/日志记录,单元测试或学习使用调试器为时已晚。

不同的工具/平台支持不同的调试技术(调试器,日志记录,单元测试等),只要开发人员熟悉其平台/工具的一些技术,除了重新检查代码,他们可能是一个熟练的开发人员,但是如果他们在调试时只有一个窍门,那么他们最终将遇到无法找到或修复的错误。

#15 楼

有很多答案,但没有提及Heisenbug?!?! ,通常
修改代码,更改变量的内存地址及其执行时间。


我只在最坏的情况下使用调试器(对于难以发现的错误)。而且,根据许多著名的开发人员/测试人员所谈论的最佳实践,最好对单元进行彻底的单元测试。这样,您可以解决大多数问题,因此无需使用调试器。

#16 楼

我最近在这里读到一个反对调试器调试的参数(或者是StackOverflow吗?)。您应该针对您的代码有测试用例。如果测试通过,则调试可能不会执行该错误(假设:您将使用与测试数据类似的数据进行调试)。

另一方面,日志记录是强制性的。如果通过测试并部署到生产环境,则可能会发现您有一个错误。该错误的证据来自过去发生的事情。即有人说:“那是怎么到达那里的?”如果没有好的日志,您将永远找不到原因。此时,甚至调试器也可能没有用,因为您不知道实际执行该错误的数据是什么样的。您需要能够从日志中调试应用程序。不幸的是,我对这个说法有很多解释,可能会对原始参数造成损害。特别是,“有重要的调试助手要花费开发时间来支持”的立场可能与调试器的重要性正交。但是,在配置中难以设置系统状态的部分使调试对于查找错误很有用,这让我深深地想到了。

#17 楼

有了良好的单元测试以及为您提供回溯的异常,您几乎不必使用调试器。

我上次使用调试程序是在某些旧应用程序中获取核心文件时。


我是“ debbuger奴才”还是这些人“太顽固”?


都不是。他们只是喜欢让生活变得更艰难的人。

#18 楼

调试只是一个优秀的开发人员应熟练使用的工具。

当然,有时候,如果您知道代码库,就可以真正知道错误的出处。但是,您也可能只需要看一遍代码就可能浪费一整天或一周的时间才能找到令人讨厌的错误。

在动态类型的语言中,无需进行某种调试(即使只是将值转储到控制台)猜测有时变得不可能。

因此,请回答您的问题-也许他们是优秀的程序员,但是他们的故障排除技能和寻找bug的能力很差。

#19 楼

取决于问题的范围。如果程序很小,并且划分得很好,那么您可以通过查看来找出答案。如果该程序是由100多人的团队在几年的时间内开发的450万行代码,那么将不可能发现某些错误。

所说程序中的那个问题(用C语言编写) )是内存覆盖。带有内存断点的调试器在错误出现后立即确定了令人讨厌的代码行。但是在这种情况下,任何人都不可能读取并保留所有450万行代码来标识某人在其数组中写过的一个点(另外,他们必须知道gargantuan程序状态的内存运行时布局)大约需要10分钟才能完成输入操作。)要点是:在小型程序或高度模块化的事情中,您可以省去调试器。如果程序真的很大又很复杂,那么调试器可以为您节省很多时间。正如其他人所说,它是一种工具,在某些情况下它比其他任何方法都优越,而在其他情况下则不是最佳选择。

#20 楼

如果该错误发生在客户端计算机上,或者其环境与您的环境有很大不同,那么设置调试器/远程调试器就很麻烦。
因此,在寒冷的日子里,您会从字段,“但是...我没有调试器”的响应无济于事。
因此,您需要开发一套技巧来解决问题并仅通过了解代码和日志来查找错误文件。

#21 楼

一堆废话:“真正的程序员不需要调试器。”也许还可以说一个真正的程序员不需要任何IDE,只需给我一个记事本和一支枯燥的铅笔即可。调试器是一种与其他工具一样可以提高工作效率的工具。

此外,请注意并非所有负责调试代码的人都对所讨论的代码熟悉。许多时间承包商进入一个他们只对发生的事情有总体理想的环境。甚至可以为它们提供环境的详细说明,或者使用20年的架构图并提供神秘的命名约定指南(尝试了解表X1234和表X4312之间具有字段F1,F2和F3的区别[是的,像这样的垃圾存在](当您是新手时),但很多时候该描述是错误的;否则,为什么会出现“神秘”错误。

作为新来的用户,您可能需要花费数小时或数天的时间进行映射,并“知道”大型数据库来解决可能要解决的问题区域然后再也不需要再看了这是浪费大量的时间和金钱。如果您可以访问调试器,则可以查看正在发生的事情,对其进行更正,然后在几分钟内就消失了。所有这些“您不需要调试器” hooey只是精英主义者的吹牛。

评论


这个稻草人没有回答所提出的问题,在任何地方都没有这样的语句:“真正的程序员不需要调试器”

– gna
13年11月14日14:10