这旨在作为一个通用问题,以帮助遇到程序问题但不知道如何使用调试器诊断问题原因的新程序员。
该问题涵盖三类更具体的问题:
- 当我运行程序时,它不会产生我期望输入的输出。
- 当我运行程序时,它崩溃并给了我堆栈跟踪。我已经检查了堆栈跟踪,但是我仍然不知道问题的原因,因为堆栈跟踪没有为我提供足够的信息。
- 当我运行程序时,它由于分段错误(SEGV)而崩溃。
这旨在作为一个通用问题,以帮助遇到程序问题但不知道如何使用调试器诊断问题原因的新程序员。
该问题涵盖三类更具体的问题:
Answers:
调试器是可以在程序运行时检查程序状态的程序。它用于执行此操作的技术手段对于理解如何使用调试器的基础并不重要。您可以使用调试器在程序到达代码中的特定位置时暂停程序的执行,然后检查程序中变量的值。在检查其变量的值时,可以使用调试器非常缓慢地运行程序,一次只运行一行代码(称为单步执行)。
调试器是一种非常强大的工具,可帮助诊断程序问题。调试器可用于所有实用的编程语言。因此,能够使用调试器被认为是任何专业或发烧级程序员的基本技能。并使用调试器自己 被认为是基本的工作,你应该做你自己问别人的帮助了。由于此站点是针对专业和发烧级程序员的站点,而不是帮助台或指导站点,因此,如果您对特定程序的问题有疑问,但没有使用调试器,那么很可能会关闭并拒绝您的问题。如果您仍然遇到类似的问题,最终将阻止您发布更多信息。
通过使用调试器,您可以发现变量是否具有错误的值,以及在程序中变量的值更改为错误的值的位置。
使用单步执行,您还可以发现控制流是否符合您的期望。例如,if
分支是否应按预期执行。
使用调试器的细节取决于调试器,并且在较小程度上取决于您使用的编程语言。
您可以将调试器附加到已经在运行程序的进程中。如果程序卡住,您可能会这样做。
实际上,从一开始,在调试器的控制下运行程序通常会更容易。
您可以通过指示应停止执行的行的源代码文件和行号,或通过指示程序应停止执行的方法/函数的名称,来指示程序应在何处停止执行(如果要以一旦执行进入方法。调试器用来导致程序停止的技术手段称为断点,而此过程称为设置断点。
大多数现代调试器都是IDE的一部分,并为您提供了一个方便的GUI,用于检查程序的源代码和变量,并带有单击界面,用于设置断点,运行程序并单步执行。
除非程序可执行文件或字节码文件包含调试符号信息和对源代码的交叉引用,否则使用调试器可能非常困难。您可能必须稍微不同地编译(或重新编译)程序,以确保存在信息。如果编译器执行了广泛的优化,则这些交叉引用可能会造成混淆。因此,您可能不得不在关闭优化的情况下重新编译程序。
我想补充一点,调试器并不总是完美的解决方案,也不应该总是调试的首选解决方案。在某些情况下,调试器可能对您不起作用:
在所有这些情况下,要么突然停止程序可能会导致最终结果有所不同,要么手动单步查找导致错误的一行会很麻烦。无论您的错误是错误的行为还是崩溃,都可能同样发生。例如,如果内存损坏导致崩溃,那么到崩溃发生时,它与第一次发生内存损坏的位置相距太远,并且没有留下有用的信息。
那么,有哪些替代方案?
最简单的就是记录和断言。在各个时间点将日志添加到程序中,然后将您得到的与期望的进行比较。例如,看看是否首先调用了您认为存在错误的函数。看看方法开始时的变量是否符合您的想法。与断点不同,没有很多特殊情况的日志行是可以的。您可以随后简单地搜索日志。一旦您遇到了与预期不同的日志行,请在同一区域中添加更多内容。使其越来越窄,直到足够小以至于无法记录错误区域中的每一行。
断言可用于在出现不正确的值时捕获它们,而不是一旦它们对最终用户具有可见的影响。越早发现不正确的值,您就越接近产生该值的行。
重构和单元测试。如果您的程序太大,则可能值得一次测试一个类或一个函数。给它输入,看一下输出,看看哪个不是您期望的。能够将错误从整个程序缩小到单个功能,可以大大缩短调试时间。
如果出现内存泄漏或内存重载,请使用能够在运行时进行分析和检测的适当工具。能够检测到实际损坏发生的位置是第一步。此后,您可以使用日志将您的方法返回到引入错误值的位置。
请记住,调试是一个倒退的过程。您得到了最终结果-一个错误-并且找到了导致该错误的原因。这是关于向后工作的方法,不幸的是,调试器只能向前走。在这里,良好的日志记录和事后分析可以为您提供更好的结果。