在chapter 5 "The Practice of Programming" Brian Kernighan和Rob Pike的{{3}}内写道:
作为个人选择,除了获得堆栈跟踪或变量或值的变量之外,我们倾向于不使用调试器。
虽然我没有关于这个主题的经验数据,但我怀疑很多程序员可能会在他们的环境中有一个可用的“调试器”中“活着”。但我也怀疑有许多高效的程序员,像Kernighan和Pike一样,避免将调试器作为第一道防线并依赖其他技术。
所以,我想问一下stackoverflow社区:
如果您是那种将“调试器”工具视为最后手段(除了获得初始堆栈跟踪)之类的程序员,那么您首先使用其他技术的原因是什么?
每个答案有一(1)个理由请更容易投票!
我还建议回答这条规则:“我不知道如何使用调试器”不是一个有效的答案。那只是无知。在做出选择之前,你应该了解你的选择!
答案 0 :(得分:15)
不通过代码使用调试器到F10 / F11可以使您成为更好的开发人员。
在我的第一份工作期间,我在Linux中进行了大量编程,而Visual Studio调试器不可用。 因为调试很难,我已经学会了如何分析我的代码并真正理解它是如何工作的。因此,我成了一名更好的开发者。
现在我只是在我查完代码并搜索“通常的嫌疑人”之后才使用调试器,如果我不理解我的代码如何运行而没有调试它,那么我重构它。
答案 1 :(得分:10)
使用调试器作为最后手段的原因是什么?
因为通常使用其他技术而不是使用调试器,我可以更快地找到错误。
答案 2 :(得分:8)
我认为Kernighan试图说明有意识的分析。在不了解森林的情况下,不要在树上潜水。也就是说,
我最喜欢的(如果这是正确的词)例子,是内存错误。在像C或C ++这样的语言中,滥用内存系统(双删除,通过死指针访问对象,运行数组的末尾)可能会破坏程序,使问题不会在任何地方出现 原因附近。
这些语言中的适当方法是使用消除这些错误的实践,但如果失败,工具也很有价值。当我遇到类似错误的经历让我怀疑“这感觉就像是一个内存错误”时,我会在之前找到valgrind,我认为“调试器”。
现在我们可以开始论证自动内存工具和边界检查库是调试器! ; ^)〜
答案 3 :(得分:7)
我更喜欢使用TDD并添加一个破坏代码的测试。然后很容易看出错误发生的位置并在没有调试器的情况下修复错误,现在我已经有了一个可以防止该错误的测试。
针对不同工作的不同工具。就像你不会将Perl用于所有内容一样,你不会为每个bug使用调试器。有时使用调试器符合问题,有时则不适用。
以我们的某个产品中出现的错误为例。它正在拉动最后一个窗口,使焦点在打印方法后重新聚焦。附加调试器后无法重新启动,但可能会在何时重新启动。最终通过良好的老式Console.Write()
语句解决了这个问题。
答案 4 :(得分:5)
转储到文件的打印语句可以在文本上操作。 grep,sort,sed和awk可以帮助解决复杂的调试问题。如果有必要,还可以编写一个小程序来解析转储的文本。
答案 5 :(得分:5)
在我看来,必须转到调试器可能是一个更深层次问题的迹象。
在启动调试器之前,我会提出针对更深层次问题的问题,如:
哪个测试失败了?如果答案是“没有测试”,那么没有对失败条件进行测试是一个需要先解决的更深层次的问题。
该例外有哪些信息? (这假设一个有例外的环境)。如果答案是“没有异常”,或者异常没有太多上下文,那么扫描吞下异常的地方,或者向异常添加更多上下文是首先要解决的更深层次的问题。
系统的该部分的构建产生了什么警告?如果您没有使用现代工具构建和分析系统以发现常见错误,并在运行时出现错误进行更正,那么您需要先解决更深层次的问题。
我们是否足够了解问题域以推断可能发生的事情?如果答案是“我们不清楚这一点”,那么与主题专家的讨论是有道理的,这些专家可以使一个系统的目的更加清晰。如果没有明确了解的要求,可能会出现更多错误。
做这些事情通常会导致至少一个错误修复,如果不是几个。这些方法具有非常有价值的副作用,强迫程序员思考关于问题,整个问题,而不仅仅是“发生错误的代码行”。
当然有些情况下,单行上发生错误,例如没有检查空指针/引用等,但现代IDE和工具有助于消除的错误类型不是那些“简单”错误吗?只需运行lint / static-analysis工具并注意警告 - 您将不再获得这些类型的错误。然后你会遇到需要人类思维推理的设计错误之类的东西 - 调试器如何解决这个问题呢?
答案 6 :(得分:2)
对于普通的小傻bug,我读取代码超过2或3次更快,在我的脑海中调试比启动调试器并使程序进入所需状态。
答案 7 :(得分:2)
打印语句倾向于产生您可以查看和推理的输出很长一段时间,而当使用调试器时,您不得不记住过去,确切地知道目前发生了什么,以及确切地说应该发生什么未来。
例如,您有一个算法可以将变量更改100次。在第85次,它被更改为在这种情况下永远不应该分配给它的值。使用调试器,您将被迫完成85次。条件断点可能会减轻其中一些。
答案 8 :(得分:2)
我绝对使用我的调试器。当我写的测试失败时,我经常会一步一步地检查我的期望与代码中显示的实际值。
话虽如此,经过多年的编程经验,您可以越来越少地需要调试器,因为您更能够“知道”问题出现的原因。
真正使调试器用户填充的两件事是:条件断点和对象检查器。它们是迄今为止最有用的调试器功能。
答案 9 :(得分:2)
不使用调试器的两个原因包括:
我不知道我是不是“那种程序员”但我不知道你想要的调试器除了:
我听说有人建议您在编写代码时使用调试器逐步执行代码,作为一种代码检查,但我不这样做。
答案 10 :(得分:2)
我更喜欢manualy跟踪代码并将日志记录到调试器。调试器使我成为一个被动的观察者,并且更难以查看整个图片。
程序执行至少有一个时间轴,在多线程环境中通常不止一个。了解之前的几个步骤以及当前执行线之后可能发生的事情有助于我跟踪错误。
答案 11 :(得分:1)
某些错误可能仅在发布模式下显示,因此可能无法使用调试器。在那些情况下,我经常使用printf:)。
答案 12 :(得分:1)
我很少使用调试器只是因为我们没有可以附加到实时系统的调试器。我们可以加载图像并使用调试器来计算结构内部的偏移量,或者将程序计数器转换为文件&电话号码。但总的来说,我们采用防御性编码,记录错误并保留大量统计数据,以便我们有机会在事后诊断失败。
拥有一个正常工作的调试器或仿真环境,偶尔会为我节省数天或数周的时间,重现,诊断。
答案 13 :(得分:1)
这取决于我的情况。使用Visual Studio我倾向于使用调试器比使用任何其他语言或操作系统编程时更多。我在Linux中编程时几乎从不使用它。通常只需更快地阅读那些搞砸并在我脑海中分析它的位。我只是在调试器出现某种奇怪的指针数组相关问题时(例如:指针数组中的第XX个条目由于某种原因是错误的),我乍看之下就看不到了。
也就是说,在Visual Studio中,除非错误是非常明显的,否则我发现扔掉断点的速度同样快,并且再次尝试我正在做的事情来看看发生了什么。
tl; dr:我使用调试器作为最后手段的原因是速度。
答案 14 :(得分:1)
我最喜欢的Visual Studio调试器功能是立即窗口。我不确定这是否在许多其他环境中提供,但它有时候很有帮助。
以下几个例子:
从数据库返回的数据未正确投射。启动调试器并尝试一些转换,直到你得到正确的一个(oops int很短或bool是一个字节等),
具有嵌套控件的Web应用程序(例如GridView中的TemplateFields)...我有一个特定控件的引用,但想要获取我所在的网格行(或反之亦然)。我可以编写一些嵌套的FindControls()并希望最好,或者我可以在即时窗口中执行它,直到找到我想要的控件。
到目前为止我工作的每个项目(仅1 - 2年的企业经验),我至少使用过一次或两次调试器/立即窗口。它可能只是说我缺乏经验,但我发现它对于很好地理解复杂系统中发生的事情非常有帮助。
答案 15 :(得分:1)
我在以下时间使用调试器:
总之,它是速度和准确性之间的平衡。但是根据经验,如果我最终花费大量时间在一段代码上,那么我很有必要回到它,所以我添加日志并添加测试,所以我不必回到它,或者我做了我所做的所有工作,以了解代码仍然存在,我可以建立在顶部。
我不喜欢调试器的一个原因是,一旦调试器关闭,我所做的所有工作都会弄清楚它是如何工作的。如果我花时间学习一段代码,我希望下次我(或其他人)能够获得这些知识。添加跟踪代码是一种非常好的方法,可以随时使用“动态注释”并随时召唤。
逍遥法外......几乎任何在运送给客户之前被移除的东西都是我回避的。如果我在我的系统周围放置一个安全网,我就没有理由在使用它时使用它,就像我在编程时一样。如果我之后必须支持它,那就更是如此......我讨厌支持,所以我想尽可能地让它变得无痛。
答案 16 :(得分:0)
当我有一个可重现的场景时,我肯定会使用调试器来追踪那个bug。 否则它基本上只用于从核心转储中获取堆栈跟踪。
跟踪是开始处理问题的基础
NB 车载嵌入式应用。
答案 17 :(得分:0)
我们的软件在Linux / Solaris / HPUX / AIX / FreeBSD / MacOS上运行,并且很难为某些平台获取调试器,并且更容易添加一些额外的跟踪代码。
答案 18 :(得分:0)
这些家伙来自unix时代,那里没有成熟的IDE。这就是为什么添加printf要比启动GDB快得多。
如今在visual studio中设置断点是最快的调试方式,所以每个人都使用
在嵌入式设备等不同平台上,将printfs设置为日志文件或类似内容仍然是最快的选择
答案 19 :(得分:0)
我只使用调试器作为最后一种可能的解决方案,因为使用调试器我经常需要跟踪大量与我的问题无关的代码。 所以我更喜欢使用我的直觉并在可能有问题的地方放置一些印刷品。有了这个,我很快解决了99.9%的错误。这里没有3天的调试!