为什么在IDE中调试更好?

时间:2009-01-09 00:04:40

标签: debugging ide

我从事软件开发已有二十多年,使用C,Perl,SQL,Java,PHP,JavaScript和最近的Python进行编程。我从来没有遇到过一个问题,我无法通过一些仔细的思考调试,以及调整良好的调试print语句。

我尊重很多人说我的技术很原始,在IDE中使用真正的调试器要好得多。然而根据我的观察,IDE用户似乎没有使用我的石刀和熊皮进行更快或更成功的调试。我真诚地愿意学习正确的工具,我从来没有被证明使用可视化调试器具有令人信服的优势。

此外,除了如何设置断点和显示变量内容的基础知识之外,我从未阅读过如何使用IDE进行有效调试的教程或书籍。

我错过了什么?是什么让IDE调试工具比周到地使用诊断print语句更有效?

您能否建议显示更好的IDE调试技术的资源(教程,书籍,截屏视频)?


甜蜜的答案!非常感谢大家花时间。非常有启发性。我投了很多票,并且没有投票。

一些值得注意的要点:

  • 调试器可以帮助我对变量,代码或运行时环境的任何其他方面进行 ad hoc 检查或更改,而手动调试则需要我停止,编辑和重新执行应用程序(可能需要重新编译)。
  • 调试器可以附加到正在运行的进程或使用崩溃转储,而通过手动调试,“重现”缺陷的步骤是必要的。
  • 调试器可以以更易读的方式轻松地显示复杂的数据结构,多线程环境或完整的运行时堆栈。
  • 调试器提供了许多方法来减少几乎所有调试任务的时间和重复性工作。
  • 可视调试器和控制台调试器都很有用,并且有许多共同的特性。
  • 集成到IDE中的可视化调试器还使您可以在单个集成开发环境(因此得名)中方便地访问IDE的智能编辑和所有其他功能。

29 个答案:

答案 0 :(得分:105)

IDE调试器将通过代码为您提供跟踪消息的一些功能的一些示例:

  • 随时查看调用堆栈,为您提供当前堆栈框架的上下文。
  • 进入库,为了添加跟踪而无法重新编译(假设您可以访问调试符号)
  • 在程序运行时更改变量值
  • 编辑并继续 - 在运行时更改代码的功能,并立即查看更改结果
  • 能够观看变量,了解变更时间
  • 能够跳过或重复代码部分,以查看代码的执行方式。这使您可以在制作之前测试理论上的变化。
  • 实时检查内存内容
  • 在引发某些例外时提醒您,即使它们是由应用程序处理的。
  • 有条件的断点;仅在特殊情况下停止应用程序以允许您分析堆栈和变量。
  • 在多线程应用程序中查看线程上下文,这可能很难通过跟踪实现(因为来自不同线程的跟踪将在输出中交错)。

总之,print语句(通常)是 static ,如果您的原始语句不够详细,则需要重新编译以获取其他信息。 IDE将删除此静态屏障,为您提供触手可及的动态工具包。

当我第一次开始编码时,我无法理解调试器的重要性是什么,我认为我可以通过跟踪实现任何功能(授权,那是在unix上,而调试器是GDB)。但是一旦你学会了如何正确使用图形调试器,你就不想再回到打印语句了。

答案 1 :(得分:34)

  • IDE调试器可让您更改 运行时变量的值。

  • IDE 调试器让你看到的值 你不知道自己想要的变量 执行开始时查看。

  • IDE 调试器可以让你看到调用堆栈 并检查的状态 函数传递了奇怪的值。 (想想这个函数是从 数百个地方,你不知道 这些奇怪的价值即将来临 来自)

  • IDE调试器可以让您 有条件地打破任何执行 根据条件指出代码, 不是行号。

  • 如果出现未处理的异常,IDE调试器将让您检查程序的状态,而不仅仅是破解。

答案 2 :(得分:16)

这是你无法使用“print”语句进行调试的一件事,就是当客户给你带来内存转储并说“你的程序崩溃了,你能告诉我为什么吗?”

答案 3 :(得分:14)

  • 通过代码打印语句会降低可读性。
  • 仅出于调试目的添加和删除它们非常耗时
  • 调试器跟踪调用堆栈,以便于查看您的位置
  • 可以动态修改变量
  • 可以在暂停执行期间执行临时命令以辅助诊断
  • 可以在IN CONJUNCTION中使用print语句:Debug.Write(“...”)

答案 4 :(得分:9)

我认为使用print语句进行调试是一种迷失的艺术,对每个开发人员来说都非常重要。一旦你知道如何做到这一点,某些类的错误比通过IDE更容易调试。知道这种技术的程序员也非常了解在日志消息中放入什么有用的信息(更不用说你实际上最终会读取日志),以便进行非调试。

那就是说,你真的应该知道如何使用逐步调试器,因为对于不同类别的bug,它更容易。我将把它留给已经发布的其他优秀答案来解释原因:)

答案 5 :(得分:6)

脱离我的头顶:

  1. 调试复杂对象 - 调试器允许您深入到对象的内部。如果你的对象有一个复杂对象数组的数组,那么print语句只会让你到目前为止。
  2. 跳过代码的能力 - 调试器还允许您跳过您不想执行的代码。没错,你也可以手动完成,但是你需要注入的代码要多得多。

答案 6 :(得分:4)

作为IDE中调试的替代方法,您可以尝试使用PHP Console的优秀Google Chrome扩展程序php library,以便:

  • 查看错误& Chrome JavaScript控制台&中的例外情况在通知弹出窗口中。
  • 转储任何类型变量。
  • 远程执行PHP代码。
  • 通过密码保护访问权。
  • 按请求分组控制台日志。
  • 跳转到错误文件:文本编辑器中的行。
  • 将错误/调试数据复制到剪贴板(对于测试人员)。

答案 7 :(得分:3)

我已经开发了近20年,但我发现使用IDE /调试器我可以:

  • 查看我可能没有想到包含在打印声明中的各种事情
  • 逐步执行代码以查看它是否与我认为需要的路径匹配
  • 将变量设置为某些值以使代码采用某些分支

答案 8 :(得分:2)

根据我的经验,简单的打印输出有一个巨大的优势,似乎没有人提到。

IDE调试器的问题是一切都是实时发生的。你在某个时间停止程序,然后你逐步完成一个步骤,如果你突然想看到之前发生的事情,就不可能回去。这与我们的大脑工作方式不一致。大脑收集信息,逐渐形成一种观点。这样做可能需要多次迭代事件,但是一旦超过某个点,就不能再回头了。

与此相反,选定的一系列打印输出/日志记录为您提供了“时间事件的空间投影”。它为您提供了所发生事件的完整故事,您只需向上和向下滚动即可轻松返回第四次。它可以很容易地回答诸如“在B发生之前发生过A”之类的问题。它可以让你看到你想要的模式。

所以根据我的经验。当单个调用堆栈中的某些内容出错时,IDE和调试器是解决简单问题的绝佳工具,并在某次崩溃时探索机器的当前状态。

然而,当我们接近更多难以解决的问题时,其中涉及到逐渐改变的状态。例如,一个算法破坏了数据结构,这反过来又导致了anohter算法失败。或者,如果我们想回答诸如“这种情况多久发生一次”之类的问题,“就像我想象的那样,按顺序和方式发生事情”。然后,“古老的”记录/打印输出技术具有明显的优势。

最好的方法是在最合适的时候使用这两种技术,例如使用日志记录/打印输出来获取一些错误,并在断点处暂停,我们需要更详细地探索当前状态。

还有混合方法。例如,当您执行console.log(对象)时,您将在日志中获得一个数据结构窗口小部件,您可以对其进行扩展并详细探索。这比“死”文本日志有很多明显的优势。

答案 9 :(得分:2)

使用IDE的一个原因可能是现代IDE支持的不仅仅是简单的断点。例如,Visual Studio提供以下高级调试功能:

  • 定义条件断点(仅在满足条件时断开,或者仅在第n次断点处的语句执行时断开)
  • 打破未处理的异常或每当抛出(特定)ecxeption时
  • 调试时更改变量
  • 通过设置要执行的下一行来重复一段代码

此外,使用调试器时,一旦完成调试,就不必删除所有打印语句。

答案 10 :(得分:2)

我很惊讶的一件事,我在另一个答案中没有看到,2个调试方法不是互斥的

即使您使用的是标准调试器(无论是否基于IDE),

printf调试也能很好地工作。特别是使用日志记录框架,您可以将已发布产品中的全部或大部分内容留下来帮助诊断客户问题。

正如此处几乎所有其他答案所述,标准调试器的关键好处在于它允许您更轻松地检查(并可能更改)程序状态的详细信息。您不必事先了解您可能想要看到的内容 - 它们都在您的指尖(或多或少)可用。

答案 11 :(得分:1)

因为你要求提供书籍指针...就Windows调试而言,John Robbins有几本关于Windows调试的好书:

Debugging Applications for Microsoft .NET and Microsoft Windows

请注意,最新版本(Debugging Microsoft .NET 2.0 Applications)仅限.NET,因此如果您需要本机代码调试(它涵盖.NET和本机),您可能需要较旧的版本(如第一个链接中) 。

答案 12 :(得分:1)

我个人认为答案很简单,“集成的调试器/ IDE可以快速为您提供大量不同的信息,而无需在命令中进行打孔。信息往往会出现在您的面前,而您却没有告诉它它告诉你什么。

可以检索信息的 ease 使得它们比命令行调试或“printf”调试更好。

答案 13 :(得分:1)

调试器相对于printf的优点(请注意,不是IDE调试器,而是任何调试器

  1. 可以设置观察点。 这是我最喜欢的查找内存损坏的方法之一

  2. 可以调试目前无法重新编译的二进制文件

  3. 可以调试需要很长时间才能重新编译的二进制文件

  4. 可以动态更改变量

  5. 可以动态调用函数

  6. 没有刷新调试状态网的问题,因此无法正确调试时序问题

  7. 调试器帮助核心转储,打印语句不要“

答案 14 :(得分:1)

因为使用print语句调试多线程应用程序会驱动你的香蕉。是的,您仍然可以使用print语句执行此操作但是您需要大量的内容并且解开语句的顺序打印以模拟多线程执行需要很长时间。

不幸的是,人脑只是单线程。

答案 15 :(得分:1)

这是我在VS.NET调试窗口中最常用的东西:

  • 调用堆栈,这也是找出别人代码的好方法
  • 当地人和手表。
  • 立即窗口,基本上是一个C#控制台,也可以让我更改变量内容,初始化东西等。
  • 能够跳过一行,将下一个语句设置为在其他地方执行。
  • 将鼠标悬停在变量上并使用工具提示显示其值的功能。

总之,它让我可以360度全方位查看执行代码的状态,而不仅仅是一个小窗口。

从来没有找过一本教这种东西的书,但是再一次,它似乎很简单,它几乎是所见即所得。

答案 16 :(得分:0)

使用IDE调试器,只要停止执行,就可以看到当前作用域中所有变量的值(一直到调用堆栈)。

打印报表可能很棒,但在任何给定位置将大量信息转储到屏幕上都可以生成整个的大量打印报表。

此外,许多IDE调试器允许您键入和评估方法,并在暂停时评估成员,这进一步增加了您必须执行的打印语句的数量。

我觉得调试器对某些语言比对其他语言更好但是...

我的一般观点是,对于像Java或C#这样的托管语言来说,IDE调试器绝对非常精彩,对于C ++非常有用,并且对于像Python这样的脚本语言并不是很有用(但可能是我没有尝试使用任何脚本语言的好调试器。)

当我进行Java开发时,我非常喜欢IntelliJ IDEA中的调试器。我在使用Python时只使用print语句。

答案 17 :(得分:0)

如上所述:调试器!= IDE。

gdb和(回到当天)TurboDebugger(独立)对他们支持的语言工作得很好[编辑],谢谢。 (或者更老的技术:Clipper调试器链接到xBase可执行文件本身) - 这些都不需要IDE

此外,尽管C / ++编码更为罕见,但printf语句有时会掩盖您试图找到的错误! (例如,堆栈上的自动变量初始化问题,或内存分配/对齐)

最后,正如其他人所说,你可以同时使用两者。一些实时问题几乎需要打印,或者至少是明智的“* video_dbg =(is_good?'+':' - ');”某处进入视频内存。我的年龄显示,这是在DOS下: - )

TMTOWTDI

答案 18 :(得分:0)

除了其他海报所说的大部分内容之外,我真的很喜欢一次一行地踩过一行,因为它迫使我一次想到一行。通常我会在没有查看变量值的情况下捕获bug,因为我在单击“下一行”按钮时被迫查看它。但是,我认为我的答案对你没有帮助,比尔,因为你可能已经掌握了这项技能。

就学习资源而言,我没有使用任何 - 我只是探索所有的菜单和选项。

答案 19 :(得分:0)

这是真正的程序员真正的问题吗?

任何人花了5分钟调试打印语句并使用IDE进行调试 - 它甚至不会询问就会对他/她产生影响!

答案 20 :(得分:0)

我已经使用了打印和IDE进行调试,我更愿意使用IDE进行调试。对我来说,唯一不适用的是时间紧迫的情况(比如调试在线游戏),你在那里用print语句丢弃代码,然后在日志文件出现严重错误之后查看它们。然后,如果你仍然无法弄明白,请添加更多打印件并重复。

答案 21 :(得分:0)

只是想在IDE中提及控制台调试器与printf和vs调试器的有用功能。

您可以附加到远程应用程序(显然,在DEBUG模式下编译)并检查其状态,使用POSIX tee实用程序将调试器输出转储到文件。与printf相比,您可以选择在运行时输出状态的位置。

当我调试部署在激进环境中的Adobe Flash应用程序时,它帮了我很多忙。您只需要定义一些在每个断点中打印所需状态的操作,使用fdb | tee output.log启动控制台调试器,然后浏览一些断点。之后,您可以打印日志并通过彻底比较不同断点的状态来分析信息。

不幸的是,这个功能[记录到文件]很少在GUI调试器中可用,这使得开发人员可以比较他们头脑中的对象状态。

顺便说一句,我的观点是,在调试调试器之前,应该计划调试的位置和调试内容。

答案 22 :(得分:0)

另外一件事是,如果你加入一个新的旧项目并且没有人真正知道代码是如何做它正在做的事情那么你就无法通过回显变量/对象来调试/ ... b / c你不知道什么代码都可以执行。

在我的工作中,我正面临着这种情况,视觉XDebuging帮助我了解正在发生的事情以及在哪里。

祝你好运

拉​​斐尔

答案 23 :(得分:0)

在错误日志和shell访问不可用的环境(例如共享主机)中,在IDE中进行调试非常有用。在这种情况下,带有远程调试器的IDE是唯一允许您执行简单操作的工具,例如查看stderrstdout

答案 24 :(得分:0)

除了已经提到的许多内容之外,调试器相对于printf的一个最重要的优点是使用printf语句假定您知道bug所在的函数。在许多情况下你没有,所以你必须做一些猜测,并将print语句添加到许多其他函数,以便本地化它。该错误可能在框架代码中或远离您认为的位置。在调试器中,设置断点以检查代码的不同区域以及不同时间点的状态要容易得多。

此外,一个不错的调试器将允许您通过将条件和操作附加到断点来执行printf样式的调试,这样您仍然可以保留printf调试的好处,但无需修改代码。

答案 25 :(得分:0)

使用print语句的一个问题是它会使代码混乱。 IE,你有一个10个部分的功能,你知道它在某个地方崩溃,但你不知道在哪里。因此,您需要添加10个额外的打印语句来确定错误的位置。一旦找到并解决了您的错误,您现在必须通过删除所有这些打印语句进行清理。也许你会这样做。也许你会忘记它,它最终会投入生产,你的用户控制台将充满调试版。

答案 26 :(得分:0)

  • 调试程序可以附加到正在运行的进程

  • 通常更容易从调试器调试线程代码

答案 27 :(得分:0)

Wauw,我喜欢这个问题吗?我从来不敢冒充......

人们似乎只有不同的工作方式。 对我来说最有效的是:

  • 拥有一个坚实的代码模型,包括内存管理
  • 使用工具(如印刷语句)来了解发生的事情。

我已经有40多年的生活编程了,每天都在C ++和Python的非平凡技术和科学应用程序中工作,我有个人经验,调试器对我没有帮助位。

我不能说这很好。我不是说不好。我只是想分享它。

答案 28 :(得分:-2)

这不只是调试。 IDE可以通过多种方式帮助您更快地构建更好的软件:

  • 重构工具
  • intellisense使api更容易被发现,或提醒确切的拼写/熟悉的物品的情况(如果你使用相同的系统15年没多大用处,但这很少见)
  • 通过自动填充变量和类名来节省输入
  • 在开始编译之前发现某些类型的错误
  • 自动跳转到变量/方法/类声明/定义,即使它们不在同一个文件或文件夹中。
  • 中断未处理的并处理例外

我可以继续。