[？ 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行会
循环之前已经执行 条件进行重新评估。
现在，在这一点上，我再次， 在这个程序结束。
我可以让这种情况继续 让我的程序自然退出。
但有几件事情 这是很重要的带走
从这个特定的讨论。
您需要评估 你自己的假设
有关如何代码应表现。
任何时候你觉得有些片 你知道发生了代码工作，
这可能是一个红旗去 回顾和评估，并确保
你的假设 如何代码运行
其实真到是怎么回事 表现在你的源代码。
>> 但更要的一点是， 当我们使用调试器，
你可以把断点 不同的代码行，
这将导致调试器 暂停执行在每个那些行
这样就可以评估 内存甚至改变它在的地方。
再次，请记住，你可以 创建多个断点，这样你
也可以继续执行，跳跃 以上代码的大部分，
它会自动暂停 在下一个破发点。
>> 实际上有更先进 调试器的特性，以及。
但是，我们必须向您推荐 一些后续的视频
要真正梳理出怎么样 使用这些特定的功能。
现在，谢谢 非常适合观看。
祝你好运调试。