[? DAN ARMADARAS:?]你好, 我[?丹Armadaras?]。 今天,我们要 在看调试。 我们不仅要 谈一些技巧, 而且我们要看看 一些特性包含 在CS50 IDE允许范围内 您可以轻松地调试程序。 只是一个例子, 东西可以去错 它实际上是什么 我们已经看到了。 在这种情况下,这是一个C程序 接受来自用户的整数, 除以它由两个,并提供了 输出给用户。 现在从我们所看到的 在前面讲课, 我们知道,这实际上会导致 特定类型的划分问题 当我们有奇数。 具体来说,我们就扔掉 小数点后的任何东西。 现在,我们知道,这 恰好是这种情况。 如果我们运行它,我们可以确认 我们的怀疑,首先,通过编译。 然后,通过运行和 输入奇数。 这是什么新鲜事。 但是,这实际上是一个 例如一个错误的 可以在一个更大的计划存在 这变得更难追查。 尽管我们知道是什么问题 是,事情的真正症结所在 可能会试图确定 具体在哪里出现错误, 确定是什么问题 是,然后将其固定。 因此,提供这作为一个例子 的可能是什么东西 我们已经知道,但可以埋 内的代码的其他元素。 所以打开这个其他来源 代码文件作为例子, 这种划分问题是现在 一个较大的程序的一部分。 不过可能有点 有点做作,和我们 可能能够容易地 识别它,特别是 因为我们刚才谈到的这一点。 但是,我们可以计算出,这 问题可以在更大的范围存在。 如果我编译这个和现在 运行它,输入一个奇数, 我们可以看到,我们没有得到准确 我们可能预期的输出。 在这种特殊情况下, 我们可以说,我们 要计算所有号码 从一个到某个特定数量。 我们可以看到,我们 有各种各样的问题 在这里,如果我们输,简单地说,0 和1时,我们提供的5输入。 因此,我们已经知道, 有一个问题在这里。 但是大家可能不知道准确 其中,这个问题确实存在。 现在的方法之一是 我们可以尝试解决这个问题 是,我们的东西 已经被引入。 我们可以只使用它在更大的规模。 在第14行,我们有 这个printf函数, 这使我们能够打印出状态 的各种信息。 这是东西,你 应在程序中利用 要尽量弄清楚到底是什么 发生在各行代码。 因此,即使这不是 我们实际上最终的输出 要生产出 这个计划,我们仍然 可能有一些调试 声明我们 可以揣摩正是 正在发生的事情对我们的代码中。 因此,在这种情况下,我会 printf输出调试标签。 在这种情况下,这是 只是一个调试字符串 说我了,把使之成为 在我的代码的输出非常明确 它是什么,我想展示。 而这里输出的数量 我们已计算。 在这种情况下,我可能会 想知道准确 发生了什么之前和 经过一些特定的计算。 所以,我可能会使用前一个printf 与该行代码后。 在这种情况下,我连 使其多一点点清晰 通过之前说的调试 并经过调试等等 我不混淆自己 多行看起来完全相同。 现在,如果我们重新编译这个和运行 它,再输入一个数字到五, 我们可以看到,我们有 现在,前后输出 并且发现,我们没有做明确的 数师或明确有 我们真正想要做的。 现在,在这种情况下,这是 不是一个真正的清晰输出。 这不是一个真正的明确的结果是 我们想从这个特定的程序。 这是,再等 有点做作。 但是,也许的事情之一 我们可以做,如果说明书说 我们希望通过划分这 2,并添加1--所以换句话说, 我们要舍up--然后 我们能知道我们能 这样做特别的事情,在这种情况下。 现在,在这里,我们知道,我们将 能加1到我们的一半数量。 让我们重新编译这个 并确认此 行为是我们想要的方式。 我们现在才看到, 有,我们有数字5。 有后,我们有3号, 它根据我们的说明书中, 就是我们想做的事。 但是,如果我们看一下 输出这里,我们可以 看到我们可能有另一个 完全错误,这是 我们是从0开始计数了。 现在再次,这是一件 我们已经看到在过去 我们可以很容易解决。 但在这种情况下,我们 也有益处 使用printf语句的 直接在for循环 准确地知道在哪里 该错误发生。 所以printf的声明 在帮助非常有用 你确定, 正是在你的源代码, 一个特定的错误发生。 而且这也是很重要的实现 如此,当我们在写代码, 大家可能都假设 约的节目的状态。 或者我们可能有假设 关于什么是计划的一部分 实际上是正确或不正确时, 后来,我们建立在该程序 并使其一部分 复杂的大型项目 我们认识到,某些方面 那其实是马车。 用printf可以真正帮助 缩小并确定 程序的区域可能不 要准确地行为的方式,我们 预计,基于我们的假设。 但是,还有其他工具 可用,还有, 这使我们能够揣摩 出在哪里发生异常 而且,具体而言,有什么事情 正在发生的程序里面。 因此,用printf非常 有用当我们要 以确定的特定区域 一个程序,有一定的缺陷。 但它也变得 一段时间后,乏味。 在这种情况下,这是一个 比较简单的程序 只有一个或两个变量。 而且它变得非常容易让我们 打印出这些变量的值 在较大的程序的情况下。 但是,我们可能有不同的 程序,有很多变数。 并且它可能不是很 这么好用的printf 尝试评估正在发生什么 对这些变量中的每一个 作为上述程序的执行。 有一个程序存在 所谓调试程序。 在这种情况下,一个我们将 使用的是GNU调试器或GDB, 这使我们能够检查内部 一个程序的在一个更运作 详细方法。 事实上,我们可以执行 GDB的命令行 在这里,只需键入GDB和 我们要调试命令。 在这种情况下,指望。 现在,在这种情况下,我们可以看到,它 给我们带来了一个提示GDB的说。 而我们实际上可以 执行命令GDB 实际开始执行的 计划,停止在某些点, 评估的变量和 检查变量 存在于程序状态 在该特定时刻, 等,等等。 它提供了大量的电力给我们。 但它只是恰巧 该CS50 IDE还 提供了一个GUI或用户 接口GDB的 使我们能够做到这一点,而无需 命令行界面任何 或甚至全部。 我可以访问的方法 是通过使用调试按钮 在CS50 IDE的最顶端。 现在,在过去,我们所拥有的 可见的是,我们使用命令 行编译,然后运行程序。 调试按钮的功能 二者的那些步骤。 但它也带来了 最右边的调试器选项卡 这使我们能够检查各种 该方案的性能的 因为它正在执行。 如果我点击调试,在此 情况下,它会弹出 在控制台中的新选项卡 窗口在最底层。 你可以看到这个选项卡 一些信息在最高层。 我们可以在很大程度上忽略了这一点。 不过的事情之一 我们要注意 是,它输出 同样的事情,我们 会得到,如果我们试图运行make在 C程序的终端窗口。 在这里,我们可以看到它的运行铛, 它有各种标志, 并且它编译我们count.c文件, 这在当时所选标签 我打的调试。 因此,这是因为非常有用的 现在用这个调试按钮, 我们可以同时编译,然后 执行程序,我们实际上 要运行。 之一是标志 重要的是,在这种情况下, 我们实际使用过 时间最长 但也只是做了一些手 挥手[听不清],其中 这是一个正确的位置。 在铿锵,它说-ggdb3。 在这种情况下,我们都 告诉铛,我们的编译器, 就是我们要编译我们的节目。 但也提供了什么 所谓的符号信息 从而使编译器实际访问 来了很多底层信息的 包含在程序中。 更具体地,数 对我有作用, 这些函数的名称, 变量,其种类 这些变量是,和各种 其他的事情,帮助调试器 执行它的操作。 现在有别的东西 这是重要的提 当我们讨论运行 一个程序以这种方式。 请注意,它实际上已经 提出了一个新的标签在我们的控制台 沿底部。 我们不再需要互动 直接与终端窗口。 但这个新选项卡 实际上是一个终端窗口。 它只是特定于运行 我们已经创建的程序。 注意,在底部,在 一些输出组合 通过铛编译器和GDB, 我们可以在很大程度上忽略了, 它实际上显示的输出 我们的节目在最底层。 现在,实现重要 这一个窗口实际上 会告诉你 从你的程序输出 但也可以接受输入 该程序,也是如此。 因此,通知,上面写着 请输入一个数字, 这是我们有相同的输出 曾在终端窗口前。 但它现在在这个新的选项卡中显示。 我可以输入一个数字。 它实际上 如我们预期的功能 向我们展示了我们的调试输出, 输出可能是越野车, 正如我们之前看到的。 而在最底层,它 实际上有一些额外的输出 从GDP中只是说 这一计划已完成。 现在,当你在这个看到的 特别是贯穿, 这是没有特别 有用的,因为即使是 虽然我们已经调试器菜单来 起来,这仍然是一个正在运行的程序。 在任何时候,它实际上 暂停执行对我们 要能够检查所有的 内包含的变量。 还有别的东西 我们必须做的,为了 让GDB认识到,我们要 暂停程序的执行 而不是只允许它继续 通常,我们会在任何其他情况下。 为了暂停执行, 在一些具体的线路, 我们需要创建什么 叫一个破发点。 而一个破发点很容易产生 在这CS50 IDE通过利用鼠标 并直接点击向左 的一些具体的行号。 一旦我做到这一点,一个红点 出现,这表明 即该行现在是一个破发点。 而下一次我运行GDB,它 将在该断点停止执行 当它到达该行代码。 现在,这是一个重要的 事情来实现 这并不一定是 情况下每行代码 实际上是访问。 如果我要创建一个函数 在这里,对于example--空F-- 而只是做一个打印线这里 - 你好 天下 - 如果我从来没有调用这个函数, 它会是这样的情况, 如果我在这里设置一个破发点, 功能将永远不会被调用。 因此,这 尤其是破发点 将实际上从未暂停 程序的执行。 所以我们可以说,我正确地创建 在编写一些代码行一个破发点 实际上将被执行。 现在,在这种情况下,这是 在主函数中第一行。 因此,这将肯定是这样的 即,只要我开始执行, 在第一行必达。 广发行将暂停执行。 然后,我就可以 与调试器进行交互。 您可以设置多条线路为 断点,如果你想。 我们也可以创建一个排队 在这里这段代码中 永远不会到达。 而且我们还可以设置一个另外的下面。 究其原因,我们将 要做到这一点,我们将 进入多一点点 详细一会儿就好了。 所以现在,我只想禁用 这些额外的破发点 这样我们就可以看看会发生什么 当我有一个单一的突破 点在我的计划。 我已经取得了一些 改变这一计划。 所以,我要保存它。 我会点击调试,这样我就可以 开始编译,然后 执行调试器。 我们将看到,时刻,后 我们选择作为断线 点以黄色突出显示。 我们还可以看到,在 右上角的调试面板 该暂停图标已经变成 进入一个小播放图标。 这意味着,我们必须暂停 执行,在该特定情况下。 而且打的播放按钮会 让我们继续执行 在该特定的点。 请注意,还有几个其他的 在此调试面板可用按钮, 为好。 步过,这让我 执行该一行代码 并加强了该行的 下一个,其中,在这种情况下, 将意味着printf的 被执行的语句。 它将然后暂停 执行第13行,像这样。 而且还有一个步骤 进入功能, 如果我创建了其他非常有用 功能在源代码中其他地方。 我想踏进 这些功能,而不是 执行该功能为一体。 但是,我们会看到更多的步骤 成在短短的一瞬间功能。 现在注意到一些其他的东西, 此调试面板中实际存在。 我们有这个小组被称为 调用堆栈,它向我们展示了 到底在哪我们。 在这种情况下,我们都在里面 的主要功能。 我们的脚本被称为count.c。 而我们正好是在 线13,列中的一个,其 正是高亮区域 的源代码指示,以及。 现在请注意,这也显示了 在局部变量节 所有的变量是 这个函数中存在。 要注意这一点很重要 所有的变量的 将出现在这个局部变量 一个函数内部分, 甚至在它们被定义。 在这里我们可以看到,我们有一个变量 中名为num,拥有默认值为0, 它是int类型的。 现在摆在我们实际初始化 所有这些变量, 我们不一定 保证看到的值为0。 和取决于其他处决 已执行 和你的内存时的状态 你真正运行这个程序, 你可能会发现你 看不到的0值 和,相反,一些其它疯狂的数字。 但不要担心。 它不会是相关的,直到 你居然初始化值。 现在,在这种情况下,我们可以看到, 我已经进行了一些输出。 而我,现在,暂停执行。 但在这种情况下,什么 我真正想做的事情 就是到现在跨过这条线 的代码,这样我实际上可以 查询该INT用户的 我们希望在我们的程序中使用。 现在,在这种情况下,当 我打了一步,通知 该暂停或者说恢复 按钮已更改为这个暂停按钮 因为这段代码实际上执行。 怎么了 现在的问题是,它是 等待我们输入一些信息 因为我们可以通过我们的输出文本见 在最底层。 所以,现在,这是 实际上没有暂停, 即使它,排序,出现 是因为什么也没有发生。 但它只是恰巧,在 我对13号线的具体情况, 我在等待用户输入。 所以GDB不能检查 一个程序,因为它正在运行。 现在,我进入了一些,下一次 input--所以我会输入数字5, 正如我们所看到的 past--按回车,我们 请注意,立即暂停GDB 并再次强调了下一行。 但现在发现,作为 我们的输入值的结果, 我们里面更新的价值 我们的局部变量,哪个 为准确地知道非常有用 是什么数为在内存中。 现在,我可以允许此程序继续运行 玩到其执行结束 击中恢复。 我们可以看到,非常快 请问程序执行完毕 具有相同的输出,我们 收到,调试器关闭, 现在这个计划 完全停止。 我表明,只有在 用途看什么 发生在我们居然打简历。 但是实际上我们要 要返回到该程序 这样我们就可以尝试调试 正是正在发生的事情。 现在,我使用调试器,我可能 并不需要这些调试printf的语句。 所以,我可以将其删除,因为我会做 现在只是回到我们的简单的代码 我们过会儿前。 现在,当我保存 编程并执行它, 它将再次进入初始 突破一点,我对11行。 而我就可以检查 我的变量我想做的事情。 它只是恰巧,这 部分是不是很有趣, 我知道,我要去 打印出这个声明。 请输入一个数字。 然后,我知道我要去 索要该整数用户。 所以,或许,其实我是想动我的 向下突破点得远一点。 您可以删除断点 通过点击,再次,直接 到的该行编号的左侧。 这红点消失时表示 这是破发点现在已经没有了。 现在,在这种情况下, 执行已暂停。 所以它实际上没有打算 恢复在该特定实例。 但是,我可以设置一个断点 在稍后一点。 当我现在恢复 代码,它会继续,并告诉 如此地步,断点。 同样,我打了简历。 似乎不喜欢什么正在发生的事情。 但是,这是因为我的 代码等待输入。 我将进入5号,敲回车,和 现在,下一个断点将受到打击。 现在,在这种情况下,这 是代码行 ,在此之前,我们就知道 正好是马车。 因此,让我们评估发生了什么 在这一时代的特殊点。 当行被突出显示,该 线尚未被执行。 所以在这种情况下,我们可以看到 我有一个数字,它 我有一个名为一个整数 NUM具有值为5。 而我要表演 一些数学上的那个数字。 如果我跳过了,就可以 请注意,对于NUM值 按照已经改变 算术,我们实际上已经完成。 而现在,我们 这个循环里面 还是现在的for循环 本身是突出, 我们可以看到,我们有一个新的 变量调用我的 将被用在该for循环。 我现在才记 提到,有时你 会看到某种疯狂 数字作为默认该号码 或者变量 实际上初始化。 我们可以看到,正是 在这里的这个可变 称为I,其具有不 尚未初始化 在突出的时间。 但是,我们可以看到,它有一定的数量 我们不会真正期望的。 没关系。 别担心 因为我们有没有真正 初始化的数量,直到我 跨过这条线与价值 我已经被初始化为值1。 所以一看就知道这其实 的情况下,让我们跳过。 现在我们可以看到,这 线已经被执行。 而我们现在强调 这个printf的线。 我们现在可以看到我们是如何价值观 i和3随时间而变化。 这是非常有用的事,事实上, 是重复步过线。 你可以找到真正是什么 碰巧你对循环内 并会发生什么变化 变量内,对于环 作为程序执行 发生一步一个脚印的时间。 现在,在这一点上,我 跨过刚好够用 那我现在在我的程序结束。 如果我跨过,它会 实际上停止执行 正如我们已经看到在过去。 让我重新启动此,再一次,所以 我可以点别的东西出来, 为好。 在这种情况下,它是 现在问我,再次, 为一个数字,其 我会再次进入。 但是这一次,我会在进入 数量较多,使得循环 将重复多次。 在这种情况下,我要去 输入值11。 现在,再次因为我设置 在第15行一个破发点, 它会高亮显示该行。 我们可以看到,我们的 11号是正确的 代表我们的局部变量。 现在跨过这一点,我们可以 看着发生在我们的I值是什么 当我们着手这里面的for循环。 它被递增每次我们 达到了顶部循环。 现在,可能的事情之一 是有用的执行过程中做 这一计划是对我来说,其实 改变变量中游看 发生在我的计划是什么。 在这种情况下,其实我可以 双击该值。 请注意,它变成了文本字段。 现在我可以进入不同的 价值共 怎么看我的程序的行为 当我改变了这一切变化。 现在,在这种情况下,可变 我现在包含值10。 但该计划仍是 在暂停执行。 当我踏上了过来,我看到 我的价值,我输入为10, 不大于num的值越大, 这立即引起了循环 停止执行。 现在,这不是唯一的 你之所以会 要修改的变量的地方。 你可能真的想 尝试修改它, 您可以继续 执行一个循环 或者,让你可以修改 之前,一些价值 达到某些特定的算术 你是要执行。 所以,现在,我们实际上改变 i的值作为程序执行, 它造成的for循环退出 ,过早地因为突然之间,我 正好是大于该值 NUM的,这意味着,对于循环 不再需要被执行。 此外,它正好是 情况下,我们改变了我的价值 该行17强调的时候, 这是在时间点 该循环执行 实际上正在评估。 如果我改变的价值 我在不同的线路,比如19, 我们会看到不同的 问题是因为19行会 循环之前已经执行 条件进行重新评估。 现在,在这一点上,我再次, 在这个程序结束。 我可以让这种情况继续 让我的程序自然退出。 但有几件事情 这是很重要的带走 从这个特定的讨论。 您需要评估 你自己的假设 有关如何代码应表现。 任何时候你觉得有些片 你知道发生了代码工作, 这可能是一个红旗去 回顾和评估,并确保 你的假设 如何代码运行 其实真到是怎么回事 表现在你的源代码。 但更要的一点是, 当我们使用调试器, 你可以把断点 不同的代码行, 这将导致调试器 暂停执行在每个那些行 这样就可以评估 内存甚至改变它在的地方。 再次,请记住,你可以 创建多个断点,这样你 也可以继续执行,跳跃 以上代码的大部分, 它会自动暂停 在下一个破发点。 实际上有更先进 调试器的特性,以及。 但是,我们必须向您推荐 一些后续的视频 要真正梳理出怎么样 使用这些特定的功能。 现在,谢谢 非常适合观看。 祝你好运调试。