[Powered by Google Translate] [第5条：不太舒服]
[内特 - 哈迪森，哈佛大学]
这是CS50。[CS50.TV]
所以欢迎回来，伙计们。
第5节。
在这一点上，已完成测验，并看到了你所做的，
希望你觉得真的很不错，因为我的分数在本节留下了非常深刻的印象。
对于我们的在线观众，我们有几个问题
有关问题上的最后两个问题 - 或测验，而。
因此，我们要对这些真的很快，让大家看到发生了什么事
以及如何通过实际的解决方案，而不是仅仅查看解决方案本身。
我们要在过去的几个问题真的很快，32和33。
只是，再次，使在线的观众可以看到这一点。
>> 如果你把你的问题是32，第13页，
13 16 32，问题是所有有关掉期。
这是所有关于交换两个整数。
这是问题，一对夫妇在演讲的时候，我们已经走了。
在这里，我们要你做的是一个快速的记忆痕迹。
要填写的值的变量，因为它们是在栈上
的代码通过这个交换功能。
特别是我们正在寻找的 - 我打算把这款iPad -
特别是，我们看到的是，这条线在这里6。
而它的编号为6的连续性与前面的问题。
我们想要做的是显示或标签的内存状态
因为它是在的时候，当我们执行这条线6号，
这实际上是一个回报，我们这里的交换功能。
如果我们向下滚动在这里，我们看到的一切都在内存中的地址提供给我们的。
这是非常关键的，我们会回来的，在短短的时刻。
然后在这里的底部，我们有一个小的内存图，我们要参考。
其实我已经做到了这一点，在我的iPad。
所以，我要交替之间来回iPad和这个代码仅作参考。
>> 让我们开始。首先，让我们把注意力集中在这里的第一对夫妇行的主要。
首先，我们要初始化x为1和y 2。
因此，我们有两个整型变量，它们都是被放置在栈上。
我们打​​算把他们的A 1和A 2。
所以，如果我翻转过来，希望我的iPad，让我们来看看 -
Apple TV的镜像，和我们走吧。好吧。
所以，如果我翻转过来，以我的iPad，
我想初始化x为1和y 2。
我们做到这一点很简单，写一个包装盒中标记为x 1
2包装盒中标记为Y。相当简单的。
所以，现在让我们回到笔记本电脑，看看接下来会发生什么。
因此，这下一行是在事情变得棘手。
我们传递的地址的x和y的地址作为参数a和b的交换函数。
x和y的地址，该地址是我们无法计算的东西，
没有这些子弹点就在这里。
幸运的是，前两个要点告诉我们的答案是什么。
x的地址是10，在存储器中，存储器中的y的地址是14。
因此，那些得到的值通过在A和B的向上顶在我们的交换功能。
所以，再一次，切换回我们的图，我可以写在10
和14 b中。
现在，这一点是我们进行交换。
所以再次翻转的笔记本电脑，
我们可以看到，交换的工作方式是我第一次解引用一个，并将结果存储在tmp。
因此，解引用运算符说，“嘿，治疗的变量a的地址的内容。
无论是存储在该地址，并加载它。“
你加载的变量将被存储到我们的TMP变量。
翻转到iPad。
如果我们去解决10，我们知道地址10是输出变x
告诉我们，因为我们的子弹点x在内存中的地址是10。
因此，我们可以去那里，得到了它的价值，这是1，因为我们看到我们的iPad，
并加载到tmp目录。
再次，这是不是最后的内容。
我们要走过和结束时，我们会得到我们的最终状态的程序。
但是现在，我们的值为1，存储在tmp。
>> 在这里有一个快速的问题。
[亚历山大]是解引用运算符 - 这只是前面的变量的明星权吗？
“是的。因此，解引用运算符，当我们打开再次回到我们的笔记本电脑，
这是明星就在眼前。
从这个意义上说，它是 - 你对比乘法运算符
这需要两件事情：解引用运算符是一元运算符。
刚刚申请到一个值，而不是一个二元操作符，
适用于两种不同的价值观。
所以，这就是发生在这条线。
我们装的值为1，并把它保存到我们的临时整型变量。
下一行，我们的内容存储的B -
或者说，我们，b是指向到的地方，一个是指向存储的内容。
如果我们分析这个由右至左，我们将取消引用b，
我们要解决14，我们要抢的整数，它是有，
然后我们要去的地址10，
我们要抛的结果，我们解引用的b到该空间。
翻转回我们的iPad，在这里我们可以使这一点更具体的，
它可能会帮助，如果我在这里写号上的所有地址。
因此，我们知道，我们在y地址为14，x是地址10。
当我们开始在B，我们解引用b，我们要抓住的价值2。
我们要抓住这个值，因为是住在地址14的值。
我们打​​算把它的变量，居住地址10，
这是正确的，相应的变量x。
所以，我们可以做一点点的覆盖这里
我们摆脱我们的1，而我们写了一个2。
因此，所有的好和良好的世界，即使我们已经覆盖x现在。
我们已经存储在我们的TMP变量x的旧值。
因此，我们可以完成交换的下一行。
翻转回我们的笔记本电脑。
现在，剩下的就是我们的临时整型变量的内容
并将它们存储到地址b被保持住在变量。
因此，我们要有效地解引用b的变量
这是在它的地址是b持有，
我们将tmp是保存它的东西的价值。
翻转到iPad一次。
我在这里可以删除此值，2，
相反，我们将复制到它的。
然后，下一行执行的，当然 -
如果我们翻回的笔记本电脑 - 这是6点，
这是我们希望我们的图完全充满。
所以，翻转到iPad，只是让你可以看到已完成的图，
你可以看到，我们在10，14在b，1 tmp中，2 x中，在y和1。
是否有任何问题吗？
这是否更有意义，已经走过吗？
就不是那么有意义吗？但愿不会。好吧。
>> 指针是一个非常棘手的问题。
与我们合作的球员之一有一个很常见的说法：
“要了解指针，你必须先了解指针。”
我认为这是非常真实的。它确实需要一段时间才能使用它。
抽签的图片，像这样的抽签的内存图是非常有帮助的，
后例如，在你走过的例子后，例如，
它会开始做一些更有意义一些更有意义和更有意义一点。
终于，有一天，你会拥有这一切完全掌握。
任何问题之前，我们进入下一个问题吗？好的。
因此，翻转的笔记本电脑。
我们的下一个问题是问题33号文件I / O。
这是一个有点放大。
问题33 - 是吗？
>> [丹尼尔]我刚做了一个简单的问题。此星或星号，
这就是所谓的提领时，你使用一个星号之​​前。
它是什么时调用之前使用的符号吗？
>>“符号前是运营商的地址。
因此，让我们向上卷动。
哎呀。我在变焦模式，所以我真的不能滚动。
如果我们看一下这段代码，在这里真的很快，
再次，同样的事情发生。
如果我们看一下这段代码，在这里，在这条线，我们拨打电话交换，
“＆”只是说“的地址在变量x的生活。”
当你的编译器编译你的代码，
它实际物理标记在内存中所有的变量住的地方。
所以编译器就可以做一次，它的编译一切，
都知道，“哦，我把x地址10地址14，我要把Y”。
然后，它可以为你填写这些值。
这样你就可以 - 它可以通过本和通＆Y以及。
这些人得到的地址，但他们也，当你通过它们的交换功能，
这此int *类型的信息，在这里，告诉编译器，
“好了，我们将要解释这个地址作为一个整型变量的地址。”
一个int作为一个地址，它是从一个字符变量的地址不同
因为一个int占用了一个32位的机器上，占用4个字节的空间，
而一个字符只占用1个字节的空间。
因此，它是重要的是要知道什么是 - 生活中，什么类型的值
住在传入的地址，进行了
或地址，你的工作。
这样一来，你知道多少个字节的信息的实际加载的RAM。
然后，是的，这种反引用运算符，就像你问，
在一个特定的地址去访问信息。
所以说，这是一个变量，在这里，治疗的内容作为地址，
去到该地址，并拉出，到寄存器加载到处理器中，负载
实际值或住在该地址的内容。
还有什么问题吗？这些都是很好的问​​题。
这是一个很多新的术语。
这也是一种时髦，看到＆和*在不同的地方。
>> 好的。
所以回到问题33，文件I / O。
这是一个发生的几件事情，我认为这些问题。
一，这是一个相当新的课题。
据介绍很快测验前，
然后，我认为这是种像这些字中的数学问题之一
，他们给你很多的信息，但实际上你不使用一吨的。
这个问题的第一部分描述的是一个CSV文件是什么。
现在，一个CSV文件，根据描述，是一个以逗号分隔值文件。
因此，这些在所有有趣的是，和你使用过他们的原因，
，因为，你们有多少人曾经使用Excel之类的东西吗？
图你最有可能，或将使用在一些点在你的生活中。
你会使用Excel之类的东西。
为了得到数据的Excel电子表格或做任何形式的处理，
如果你想编写一个C程序或Python程序，Java程序，
处理的数据储存在那里，
把它弄出来的最常见的方式之一，是一个CSV文件中。
，你可以打开Excel中，当你去到“另存为”对话，
你可以得到一个实际的CSV文件。
>> 很方便的知道如何处理这些事情。
它的工作方式是，它是类似的 - 我的意思是，它本质上是模仿电子表格，
在那里，我们在这里看到，最左边的一块，
我们所有的姓氏。
因此，我们有马兰，那么哈迪森，然后鲍登，MacWilliam，然后生。
所有的姓氏。然后一个逗号分隔的姓氏第一个名字。
大卫奈特，抢劫，张宇，和Zamyla。
我总是混淆：罗比和汤姆。
然后，终于，第三列是电子邮件地址。
一旦你了解了，其余的程序是非常简单的实现。
我们所做的，以模仿相同的结构在我们的C程序
是我们使用了一个结构。
我们将开始播放这一点以及。
我们看到了他们的第一个问题集3，当我们正在处理的字典点点。
但是，这名员工结构存储一个姓氏，一个名字，和电子邮件。
就像我们的CSV文件存储。
因此，这只是从一种格式转换到另一个。
我们必须转换，在这种情况下，工作人员成一条线的结构的，
一个以逗号分隔的行，就这样。
这是否有意义吗？你们都采取了有奖问答，
所以我想你已经至少有一些时间来思考这个问题。
>> 在租用功能，这道题我们 - 我们将在这一点位变焦 -
在人员结构，人员结构，名称为s，
并附加其内容到我们的staff.csv文件。
事实证明，这是非常简单的使用。
我们将发挥着这些功能，今天多一点点。
但是，在这种情况下，fprintf函数是真正的关键。
因此，fprintf，我们可以打印，就像你们一直在使用printf的整个任期。
您可以输出到一个文件中的行。
因此，而不是只是通常的printf调用，你给它一个格式字符串
然后你替换所有的变量与下面的参数，
与fprintf，你的第一个参数，而不是你要写入的文件。
如果我们要看看这个设备，例如，人fprintf，
我们可以看到的printf和fprintf的区别。
我将在这里一点点放大。
因此，用printf，我们给它一个格式字符串，然后在随后的参数
更换或替换成我们的格式化字符串中的所有的变量。
而与fprintf，第一个参数是事实上，这称为流文件*。
>> 在这里，我们的租赁，
我们已经为我们打开了我们的文件*流。
那这是什么第一线;它打开了staff.csv的文件，
它以附加的方式打开它，并为我们做了所有剩下的是
编写人员结构的文件。
而且，让我们来看看，我想使用iPad？
我会使用iPad。我们有无效的 - 让我们把这个放在桌子上，这样我就可以写一个好一点的 -
无效租用，它需要一个参数，称为s的人员结构。
得到了我们的大括号，我们有我们的文件*文件，
我们有我们的FOPEN线给我们，
，我就写点，因为它已经在百科。
然后在我们的下一行，我们要拨打电话为fprintf
和我们将要通过的文件中，我们要打印的，
然后我们的格式化字符串，其中 -
我会告诉你们告诉我它是什么样子。
你怎么样，Stella吗？你知道的格式字符串的第一部分看起来像什么？
斯特拉]我不知道。 >>随意问吉米。
你知道吗，吉米？
[麦]这是最后一个吗？我不知道。我不能完全肯定。
“好了。怎么样，有没有人得到这个正确的成绩？
所有权利。
原来，这里所有我们需要做的是，我们希望我们的人员结构各部分的
作为一个字符串到我们的文件被打印出来。
我们只是使用字符串替换字符，三个不同的时间，因为我们有个姓
其次是逗号，然后一个名字后跟一个逗号，
然后最后的电子邮件地址，其次是 - 这是不
适合我的屏幕上 - 但它后面的一个换行符。
所以我要去写它只是出现了下滑。
然后按照我们的格式化字符串，
我们只需要换人，我们使用点表示法访问
我们所看到的问题集3。
我们可以使用s.last，s.first，s.email
在这三个值到我们的格式化字符串来代替。
所以，这是怎么去的？有意义吗？
是吗？不是吗？有可能吗？好吧。
>> 最后一点，我们做印刷后，我们已经打开的文件后，我们已经：
每当我们打开一个文件，我们总是要记住关闭。
否则，我们将最终的内存泄漏，
使用文件描述符。
因此，要关闭它，我们使用哪些功能？丹尼尔？
[丹尼尔] FCLOSE？ >> FCLOSE，准确。
因此，这个问题的最后一部分是正确地关闭文件，使用fclose函数，
它只是看起来像。
太疯狂了。
酷。
所以这就是问题的测验33。
I / O来了，我们肯定将有更多的文件。
我们会做多一点点在今天的演讲，或在今天的部分，
因为这是怎么回事形成这个即将到来的pset中的大部分。
让我们继续在这一点上测验。是吗？
>> [夏洛特]为什么FCLOSE（文件），而不是FCLOSE（staff.csv）？
“啊。因为事实证明 - 这样的问题，这是一个伟大的，
正因为如此，当我们写FCLOSE，我们，写FCLOSE（文件）明星变量
而不是在文件名的，staff.csv？是正确的吗？是啊。
因此，让我们一起来看看。如果我切换回我的笔记本电脑，
让我们来看看fclose函数。
因此，fclose函数关闭一个流，它需要的指针，我们要关闭的流，
而不是实际文件名，我们要关闭。
这是因为在幕后，当你拨打电话的FOPEN，
当你打开一个文件时，你实际上是在分配内存来存储信息的文件。
所以，你必须有信息的文件的文件指针，
如它是开放的，它的大小，在那里你目前在该文件中，
这样就可以在文件中读取和写入到特定的地方调用。
您最终成交的指针，而不是关闭的文件名。
>> 是吗？ [丹尼尔]因此，为了使用租用，你会说 - 它是如何获得用户输入？
fprintf像在这个意义上，它会等待用户输入的GetString
要求用户输入 - 等待你输入这三样东西？
或者你需要使用的东西，落实租？
>>呀。因此，我们不 - 的问题是，如何得到用户输入
为了实现租吗？我们这里是出租的呼叫者，
在这种人员结构，所有的数据存储结构已经通过。
因此，fprintf是只是数据直接写入到文件。
有没有等待用户输入。
用户是否已经输入适当的把它在这名员工结构。
的事情，当然，将打破，如果这些指针为空的，
因此，我们在这里向后滚动，我们期待在我们的结构。
我们有字符串，字符串的第一个字符串的邮件。
现在我们知道，所有的那些真正的，引擎盖下，是char *变量。
这可能会或可能不会被指向空。
他们可能指向堆内存，
也许内存堆栈。
我们真的不知道，但如果其中任一指针是空的，或无效的，
一定会崩溃，我们的租赁功能。
这是什么，这是一种超越的考试范围。
我们不担心这一点。
大。好吧。因此移动从测验。
>> 让我们来关闭这个家伙，我们要看看在pset中4。
所以，如果你们看在pset规范，一旦你可以访问它的，cs50.net/quizzes，
我们要经过几个部分问题，今天。
我向下滚动 - 部分问题开始的第三页上的pset的规范。
第一部分要求你去观看短重定向和管道。
这是一个很酷的短，显示出一些新的，很酷的命令行技巧，您可以使用。
然后，我们有几个问题要问你。
这第一个问题，关于流，输出写入默认情况下，
一种感动刚才只是一点点。
我们刚才谈到的这fprintf需要在一个文件中*流作为它的参数。
FCLOSE需要在一个文件中*流为好，
和返回值的FOPEN给你一个文件*流以及。
究其原因，我们还没有看到那些之前我们已经处理了printf
是因为输出有一个默认的流。
而它的默认流写
你会发现在短期内。
所以，一定要看看它。
>> 在今天的部分，我们要谈一点关于GDB
，因为你是比较熟悉的，它更多的练习，你得到它，
你能够更好地将追捕在自己的代码错误。
这加快了极大的调试过程。
因此，通过使用printf，每次你这样做，你必须重新编译你的代码，
你必须再次运行它，有时你必须左右移动printf调用，
注释掉的代码，它只是需要一段时间。
我们的目标是尝试说服你，你基本上可以用GDB，
在任何时候在你的代码中的printf什么，你永远不会有重新编译它。
你从来没有开始，并不断猜测的printf下。
要做的第一件事是复制这条线和部分代码的网站。
我说，“wget的http://cdn.cs50.net”这行代码复制。
我要复制它。
我要到我的设备，缩放，所以你可以看到我在做什么，
将其粘贴在那里，当我按下Enter键，这个wget命令从字面上是一个网络获得。
这是怎么回事拉下这个文件在互联网上传播，
它的将它保存到当前目录中。
现在，如果我列出当前目录中，你可以看到，我已经了这section5.zip的文件右键在那里。
那家伙处理的方式是将其解压缩，
您可以在命令行中，就是这样。
Section5.zip。
将它解压缩，创建文件夹对我来说，
膨胀中的所有内容，让他们在那里。
所以，现在我可以进入我的第5节目录使用cd命令。
清除屏幕采用了明确。因此，清除屏幕。
现在我有一个干净漂亮的终端处理。
>> 现在，如果我列出我看到在这个目录中的所有文件，
你看到我已经得到四个文件：buggy1，buggy2，buggy3，并buggy4，。
我也得到了相应的文件。
我们不会看的。c文件。
相反，我们要使用它们时，我们打开了GDB。
我们已经把他们周围，使我们有机会获得实际的源代码，当我们使用GDB，
这部分的组成部分，但我们的目标是修改周围的GDB
看看我们如何能够用它来找出什么地方出了错这四个错误的程序。
所以我们仅仅是在房间里真的很快，
我要问别人的bug的程序运行一个，
，然后我们会去通过GDB作为一个群体，我们将看看我们能做些什么来解决这些计划，
或者至少确定发生了什么事情错在他们每个人。
让我们从这里开始与丹尼尔。你会运行buggy1？让我们看看会发生什么。
丹尼尔说，有一个应用程序错误。 >>呀。没错。
所以，如果我运行buggy1，我得到了赛格故障。
在这一点上，我可以去开拓buggy1.c，
试图找出什么地方出了错，
但该段故障错误最厌恶的事情之一
的是，它不会告诉你哪一行的程序的东西实际是错误的，并打破。
你有看的代码
找出猜测检查或printf来看看会发生什么错误。
关于GDB的最酷的事情之一是，它是真的，真的很容易
要弄清楚的线在你的程序崩溃。
这是完全值得的，使用它，即使只是。
启动GDB，我输入GDB，然后我给它的，我想运行的可执行文件的路径。
在这里，我打字：GDB ./buggy1。
按下回车键。给了我这一切版权信息，
在这里你会看到这条线，说：“从/ home /读取符号
jharvard/section5/buggy1。“
如果一切顺利的话，你会看到它看起来像这样打印出的信息。
它会读取符号，它会说：“我从你的可执行文件，读取符号”
，然后将这个“做”的消息在这里。
如果你看到一些其他的变化，你看到它找不到符号
或类似的东西，这是什么意思，你只需要编译你的可执行文件。
当我们使用GDB编译程序，我们必须使用特殊的-g标志，
，这就是默认情况下，如果你编译你的程序，通过键入make
或车或恢复，任何人。
但是，如果你正在编译手动铛，那么你就必须去，其中包括，使用-g标志。
>> 在这一点上，现在，我们有我们的GDB提示，
这是很简单的运行程序。
我们可以键入运行，我们就可以输入r。
大多数GDB命令都可以缩写。
通常只是一个或几个字母，这是相当不错的。
因此，萨阿德，如果你输入r，并按下Enter键，会发生什么呢？
萨阿德我SIGSEGV，分割故障，那么这一切的官样文章。
>>呀。
就像我们在屏幕上看到，现在，像萨阿德说，
当我们键入run或r并按下回车键，我们仍然得到同样的赛格故障。
因此，使用GDB不解决我们的问题。
但它为我们提供了一些官样文章，而事实证明，这种官样文章
实际上告诉了我们它的发生。
要解析这一点，第一位的功能是，在这一切是怎么回事了。
有__ strcmp_sse4_2，它告诉我们发生了什么事在这个文件中的
被称为sysdeps/i386，这一切，再次，种混乱 - 而行254。
这是一种很难解析。通常，当你看到这样的东西，
这意味着它在一个系统库段断层。
因此，做的strcmp。你们已经看到的strcmp。
不是太疯狂了，但是这是否意味着STRCMP破损或STRCMP，有一个问题吗？
你怎么想，亚历山大？
[亚历山大]是 - 是254的行吗？ - 不是二进制的，但是这不是他们的天花板，
然后有每个函数的另一种语言。 254在该函数中，或者 - ？
>>这是第254行。它看起来像。文件，所以它的汇编代码可能。
>> 但是，我想更紧迫的事情，因为我们已经得到了赛格故障，
它看起来像它来自strcmp函数，
，那么，这是否意味着strcmp的坏了？
它不应该有希望。因此，仅仅因为你有一个分割故障
系统的功能之一，通常这意味着，你只是还没有被称为是正确的。
的事情弄清楚什么是怎么回事
当你看到这样的疯狂的东西，每当你看到一个段故障，
特别是如果你有一个程序，使用多只主，
使用的回溯。
我写BT，而不是全面的回溯字缩写回溯。
但是，夏洛特，当你键入BT并按下回车键，会发生什么呢？
夏洛特它让我看到两行，0行和第1行。
>>呀。所以第0行和第1行。
这些是实际的栈帧目前在播放的时候，你的程序崩溃。
从最上面的帧，帧0开始，并持续到最底层的，这是第1帧。
我们的最高框架是strcmp的框架，。
你可以认为这是类似的问题，我们只是在做测验的指针，
我们交换堆栈帧上的主栈帧的顶部，
和我们的互换使用，主要使用的变量的顶部上的变量。
在这里我们的崩溃发生在我们的strcmp函数，这被称为我们的主要功能，
回溯，不仅给我们的功能在里面的东西没有，
但它也告诉我们，这里的一切，被称为。
所以，如果我多一点点滚动的权利，
我们可以看到，是的，我们在这个的strcmp，sse4.s文件的254行。
但有人呼吁在buggy1.c，6号线。
因此，这意味着我们可以做的 - 是我们可以去看看，看看发生了什么事
在6号线buggy1.c。
同样，也有几种方法可以做到这一点。一个是退出GDB
或者你的代码在另一个窗口中打开和交叉引用。
，其本身而言，是非常方便的，因为现在，如果你在办公时间
和你已经有了一个段故障和TF想知道一切都被打破，
你可以说，“哦，第6行，我不知道这是怎么回事，
但6号线是什么导致我的计划打破。“
做到这一点的另一种方法是，你可以使用此命令列表中GDB。
您也可以将其缩写为升。
因此，如果我们打升，我们在这里吗？
我们得到了一大堆怪异的东西。
这是实际的汇编代码
是在strcmp_sse4_2。
这看起来有点时髦的，
的原因，我们正在这是，因为现在，
GDB我们在第0帧。
>> 所以，任何时候我们看变量，任何时候我们来看看源代码，
我们正在寻找源代码的堆栈帧，涉及到我们目前所在
因此，为了获得什么有意义的事情，我们必须
转移到更有意义的栈帧。
在这种情况下，主要的堆栈帧将使一些更有意义，
因为这实际上是我们写的代码。
strcmp的代码。
的方式，可以在帧之间移动，在这种情况下，因为我们有两个，
我们有0和1，
你这样做的up和down命令。
如果我把一帧，
现在，我在主堆栈帧。
我可以向下移动到的地方，我回去，
再上去，走了，再上去。
如果你这样做你的程序在GDB，你得到一个崩溃时，你会得到回溯，
你看到它的一些文件，你不知道发生了什么事情。
您尝试列表，代码并不很熟悉你，
一起来看看在您的框架，并找出你在哪里。
你可能在错误的堆栈帧。
或者至少，你是不是你真的可以调试堆栈帧中。
现在，我们在适当的堆栈帧，我们在主，
现在我们可以使用list命令可以计算出该行是什么。
你也可以看到它，它打印我们就在这里。
但是，我们可以打列出所有相同，列表，为我们提供了这个漂亮的打印输出
实际的源代码，在这里是怎么回事。
>> 特别是，我们可以看一下在第6行。
我们可以看到，在这里发生了什么事情。
它看起来像我们正在做的字符串比较
字符串“CS50石头”和argv [1]。
他到这里来是崩溃。
所以，大小姐，你有什么想法什么可能会在这里？
[大小姐]我不知道为什么它的崩溃。 >>你不知道为什么它的崩溃？
吉米，有什么想法吗？
[麦]我不能完全肯定，但我们最后一次使用的字符串比较，
或strcmp的，我们有三种不同的情况下。
我们没有一个==，我​​不认为，正确的，第一行。
相反，它被分离成三个，一个是== 0，
1 <0，我觉得，一个是> 0。
因此，也许这样的事情？ >>呀。所以有这个问题
我们做的比较正确？
Stella吗？有什么想法？
斯特拉]我不知道。 >>不知道。丹尼尔？思考？好吧。
事实证明，所发生的事情在这里是当我们运行程序时，
我们得到了赛格故障，当你第一次运行的程序，丹尼尔，
你给它任何命令行参数？
[丹尼尔]第>>号在这种情况下，是什么时，argv [1]？
>>没有任何价值。 “没错。
那么，有没有适当的字符串值。
但有一定的价值。被存储在那里的​​价值是什么？
>> A垃圾的价值呢？ >>它要么是一个垃圾值，或者，在这种情况下，
argv数组结束始终与null的终止。
所以实际上存储在那里是空的。
另一种方法解决这个问题，而不是想着它通过，
是要设法把它打印出来。
这是我说的，使用GDB是伟大的，
因为你可以打印出所有的变量，所有你想要的值
使用这种方便，花花公子p命令。
所以，如果我键入p，然后我输入一个变量的值或变量的名称，
说是argc，我看到argc是1。
如果我想打印出来的argv [0]，我可以这样做，就这样。
就像我们所看到的，ARGV [0]是你的程序的名称，
总是的可执行文件的名称。
这里你可以看到它有完整的路径名。
我还可以打印出的argv [1]，看看会发生什么。
>> 在这里，我们得到了这种神秘的值。
我们拿到的这款0x0的。
记得在学期开始时，我们谈到十六进制数字吗？
或者说，在年底的pset 0的小问题如何表示十六进制50？
我们编写的十六进制数在CS，只是为了不混淆自己
带小数点的数字，是我们始终以0x前缀。
因此，这0x前缀始终只是解释为十六进制数，
而不是字符串，而不是作为一个十进制数，而不是作为一个二进制数。
由于5-0是一个有效的十六进制数。
这是一个十进制数，50。
因此，这是我们如何消除歧义。
因此，0x0的装置，十六进制的0，这也是十进制0，二进制0。
只是，它的值为0。
事实证明，这是空的，其实是在内存中。
NULL就是0。
在这里，存储元件的argv [1]为null。
因此，我们试图比较“CS50石头”字符串为空字符串。
因此，提领空，试图访问空的东西，
通常会造成某种分割故障或其他不好的事情发生。
而事实证明，STRCMP不请检查
您是否已经通过了一个值，该值是空的。
相反，它只是前进，试图做的事情，
，如果段故障，段故障，这是你的问题。你必须去解决它。
真的很快，我们会如何解决这个问题？夏洛特？
[夏洛特，您可以检查使用的话。
因此，如果argv [1]为null == 0，则返回1，什么不知所云。
>>呀。所以这是一个伟大的方式来做到这一点，我们可以查看，
我们的价值传递到strcmp的，ARGV [1]，它是空？
如果是null，那么我们可以这样说好了，中止。
>> 更常见的方式做，这是使用的argc的值。
在这里你可以看到在开始的主，
我们省略了，第一，我们通常做测试时，我们使用命令行参数，
这是我们ARGC值测试是否是我们所期望的。
在这种情况下，我们预计至少需要两个参数，
的程序名加一个其它。
因为我们使用的第二个参数在这里。
因此，有一些测试的结果之前，在我们的strcmp呼叫
argv是，测试是否至少2个，也做了同样的事情。
我们可以看到，如果再次运行程序。
您可以随时重新启动你的程序在GDB中，这是非常好的。
您可以运行，而当你传递参数给你的程序，
通过他们，当你调用运行，而不是当你启动GDB。
这样，你可以每次都用不同的参数调用程序。
所以运行，或者，我可以输入r，，让我们看看会发生什么，如果我们输入“你好”。
它总是会问你，如果你想从一开始就再次启动。
通常情况下，你想从一开始就再次启动它。
而在这一点上，它会重新启动一次，它打印出
该程序，我们正在运行，buggy1的说法您好，
并打印出这个标准了，它说，“你得到一个D，”悲伤的脸。
但是，我们不段错误。它说，过程正常退出。
所以，看起来还不错。
没有更多的赛格故障，我们做了过去，
所以看起来这的确是段故障错误，我们得到。
不幸的是，它告诉我们，我们需要一个D.
>> 我们可以回去看一下代码，看看在那里发生了什么事情
要弄清楚什么是 - 为什么它告诉我们，我们得到了D.
让我们来看看，这里被这个printf说，你有D.
如果我们输入列表，你继续输入列表，它不断迭代，通过你们的节目，
因此，它会告诉你你的程序的前几行。
然后，它会告诉你在接下来的几行字，下一个块和下一块。
它会继续尝试下去。
现在，我们会得到“线16号超出范围。”
因为它只有15行。
如果你到了这一点，你想知道，我该怎么办？“你可以用help命令。
使用帮助“，然后给它一个命令的名称。
你看到GDB为我们提供了这样的东西。
它说，“不带参数，列出了10个多行后或以前上市。
列表 - 列出了前十行 - “
所以，让我们尝试使用列表中减去。
这列出的前10行，你可以玩弄名单一点点。
你可以做的列表，列表 - ，你甚至可以给列出一个数字，如清单8，
，它就会列出了10条线，8号线附近。
你可以看到在这里发生了什么事，是你已经有了一个简单的if else。
如果您键入在CS50岩石，它会打印出“你会得到一个A.”
否则，它会打印出“你得到了D.”
游民镇。好的。是吗？
>> [丹尼尔]因此，当我试图这样做CS50不带引号的岩石，
它说：“你得到了D.”
我需要引号来得到它的工作，这是为什么？
>>呀。事实证明，当 - 这是另一个有趣的小花絮 -
当您运行该程序，如果我们运行和输入CS50岩石，
就像丹尼尔说他这样做，按Enter键，
它说，我们得到了D.
而问题是，这是为什么？
而事实证明，我们的终端和的GDB解析这些作为两个单独的参数。
因为当有一个空间，这暗示
第一个参数结束，即将开始的下一个参数。
结合的方式一分为二，或遗憾的是，成为一个参数，
使用引号。
所以现在，如果我们把它放在引号并再次运行它，我们可以得到一个A。
因此，只要回顾一下，没有引号，的CS50和岩石被解析为两个独立的参数。
随着行情中，它作为一个参数完全解析。
>> 我们可以看到这一个断点。
到目前为止，我们已经运行我们的程序，它的运行
直到它赛格故障或点击错误
或直到它已退出所有已经完全正常。
这并不一定是最有帮助的事情，因为有时
你有一个错误在你的程序中，但它不是一个分割故障导致。
它不会导致程序停止或类似的东西。
GDB的方式来获得你的程序在一个特定的点暂停
是设置一个断点。
你可以做到这一点，一个函数名上设置一个断点
或者您可以设置一个特定的代码行上设置一个断点。
我想设置断点的函数名，因为 - 简单易记，
如果你真的一点点去改变你的源代码，
那么你的断点将留在你的代码在同一个地方。
然而，如果你使用的是行号，行号更改
因为你添加或删除了一些代码，然后断点都完全搞砸了。
其中最常见的事情，我做的是设置一个断点上的主要功能。
我经常会启动GDB，我会键入b主，按下回车键，将设置一个断点
的主要功能，只是说，“暂停程序尽快开始运行，”
这样一来，当我运行我的程序，比方说，CS50岩石的两个参数
并按下回车键，得到的主要功能，它就停在第一线，
前strcmp函数求值。
>> 由于我停顿了一下，现在我可以开始摆弄周围，看是怎么回事
所有的不同的变量传递到我的程序。
在这里，我可以打印出argc和看看会发生什么。
请参阅argc是3，因为它有3个不同的值。
它得名的程序，它得到的第一个参数，第二个参数。
我们可以打印在ARGV [0]，ARGV [1]和argv [2]。
所以，现在你也可以看到为什么这STRCMP调用是要失败的，
因为你看到，它并分裂成两个独立的参数的CS50和岩石。
在这一点上，一旦你已经打了断点，你可以通过你的程序继续加强
一行行，而不是重新启动程序。
所以，如果你不想再次启动程序，只是继续从这里开始，
您可以使用continue命令，并继续将运行该程序的结束。
就像它在这里做的。
不过，如果我重新启动程序，CS50岩石，它打我的断点再次，
而这一次，如果我不想去，一路过关斩将，其余的程序，
我可以用下一个命令，这也是我缩写与n。
而这将逐步的程序行线。
所以，你可以看的东西执行，作为变量的变化，事情得到更新。
这是相当不错的。
其他很酷的事情，而不是一遍又一遍，再重复同样的命令，
所以在这里，如果你只需要敲击回车 - 你看，我没有在任何输入 -
如果我只是打回车键，将重复前面的命令，
或以前的GDB命令，我刚刚放进去。
我可以保持按下回车键，它会通过我的代码行的行不断加强。
我会鼓励你们去看看其他bug的程序。
我们没有时间去通过所有这些今天在第。
源代码是存在的，所以你可以看到这是怎么回事
如果你真的卡幕后，
但最起码，练习启动GDB，
运行该程序，直到它打破了你，
回溯，搞清楚什么样的功能崩溃，
哪一行，打印出一些变量的值，
只是让你的感觉，因为这将真正帮助你前进。
在这一点上，我们要退出的GDB，你退出或者只是q。
如果你的程序的运行仍然在中间，并没有退出，
它总是会问你：“你确定你真的要退出吗？”
您可以打是肯定的。
>> 现在，我们要看看我们的下一个问题，这是猫的程序。
如果你看重定向和管道短，你会看到汤米使用这个程序
基本上所有输出的文件打印到屏幕上。
所以，如果我遇到猫，这实际上是一个内置的程序到设备，
，如果你有Mac电脑，你可以做到这一点在您的Mac上，如果你打开​​终端。
而我们 - 猫，比方说，cp.c，并按下回车键。
这是什么做的，如果我们向上滚动一点点，看到我们跑线，
我们跑了cat命令，它实际上只是打印出来的内容cp.c我们的屏幕上。
我们可以再次运行它，你可以把多个文件一起。
所以，你可以做猫cp.c，那么我们也可以连接起来的cat.c的文件，
这是我们写的程序，
它会同时打印文件备份到我们的屏幕上。
因此，如果我们向上滚动一点点，我们看到，当我们跑了这猫cp.c，cat.c，
首先它打印出来cp文件，然后在下面的，它打印出cat.c的文件就在这里。
我们将用此来只是让我们的脚湿。
玩简单的打印到终端，请参阅如何工作的。
如果你们打开用gedit cat.c，按Enter键，
你可以看到我们写的程序。
我们这个漂亮的锅炉板，所以我们不必花时间打字了这一切。
我们还检查传入的参数
我们打​​印出不错的用法消息。
>> 这是诸如此类的事情，再次，就像我们一直在谈论，
这几乎就像肌肉记忆。
只记得继续做同样的东西
总是打印出某种有用的消息
让人们知道如何运行你的程序。
随着猫，它是非常简单的，我们只是要通过不同的参数
被传递到我们的计划，我们要打印
其内容在一个时间的一个屏幕。
为了打印文件输出到屏幕上，我们要做的东西非常相似
我们所做的，在测验结束时。
在测验结束，聘用程序，我们要打开一个文件，
然后我们有打印到它。
在这种情况下，我们要打开一个文件，我们要读取它，而不是。
然后我们要打印的，而不是到一个文件中，我们要打印到屏幕上。
所以打印到屏幕上，你用printf之前全部完成。
所以这是不是太疯狂了。
但是，读取一个文件是一种奇怪的。
我们将通过那么一点点的时间。
如果你们回去，最后一个问题，您的测验，问题33，
我们要在这里做的第一行，打开文件，非常类似于我们在那里做了什么。
因此，斯特拉，是什么样子，当我们打开一个文件时，该行吗？
[特拉]资本FILE *文件 - “”好了。 >> - 是平等的FOPEN。 “是啊。
在这种情况下是什么？这是在注释中。
>>这是在评论吗？的argv [i]的和r？
“没错。正确的。斯特拉是完全正确的。
这是行看起来像什么。
我们将得到一个文件流变量，它存储在一个FILE *，所以全部大写字母，
将文件FILE *，这个变量的名称。
无论我们喜欢，我们可以把它称为。我们可以把它first_file，或file_i的，无论我们想要的。
在命令行上这个节目，然后通过的文件名。
因此，它存储在argv [我]，然后我们要打开这个文件在读取模式。
现在，我们已经打开了文件，有什么事情，我们总是要记住做
每当我们打开一个文件？关闭它。
所以，大小姐，我们该如何关闭一个文件？
[大小姐] FCLOSE（文件）>> FCLOSE（文件）。没错。
大。好吧。如果我们看一下在这里做评论，
它说，“开放的argv [i]和它的内容打印到stdout。”
>> 标准输出是一个奇怪的名字。 stdout是我们的一种说法
我们要打印到终端，我们希望将其打印到标准输出流。
事实上，我们可以摆脱这个评论就在这里。
我要复制并粘贴它，因为这是我们所做的。
在这一点上，现在我们要读取的文件的点点滴滴。
我们已经讨论了一对夫妇的读取文件的方式。
哪些是你最喜欢这么远吗？
哪种方式，你见过，你还记得，读文件？
[丹尼尔·弗里德？ >>弗里德？因此，弗里德是其中之一。吉米，你知不知道什么人吗？
[麦]第>>好。不。夏洛特？亚历山大？还有其他人吗？好吧。
因此，其他的是fgetc，是我们将使用一个不少。
还有fscanf，你们看到这里的模式吗？
他们都以f。任何一个文件。
有FREAD，fgetc函数，fscanf。这些所有的读出功能。
为写我们FWRITE，我们的fputc，而不是fgetc函数。
我们也fprintf，像我们看到的测验。
由于这是一个问题，涉及从文件中读取，
我们将使用这三个功能中的一个。
我们不打算使用这些功能。
这些功能都在标准I / O库。
所以，如果你看一下这个程序的顶部，
你可以看到，我们已经包含标准I / O库的头文件。
如果我们想弄清楚，我们要使用哪一个，
我们可以随时打开的手册页。
因此，我们可以输入man的stdio
并阅读所有的的stdio的输入和输出功能C.
我们已经可以看到啊，看看。提fgetc，它的一提的fputc。
所以，你可以向下一点点，看，说，fgetc函数
看它的man page。
你可以看到，它伴随着一大堆的其他功能：
fgetc函数，FGETS，getc函数，getchar函数，获取，ungetc的函数，其输入的字符和字符串。
因此，这是我们如何从标准输入文件读取的字符和字符串，
这本质上是从用户。
这是如何做到这一点，在实际C.
因此，这是不使用的GetString和getchar功能
我们使用的从CS50库。
我们要做的这个问题的几种方法
所以，你可以看到两种不同的方法做这件事。
fread函数，丹尼尔提到的和fgetc函数都不错的办法做到这一点。
我认为fgetc函数是一个容易一些，因为它只有你可以看到，
一个参数，FILE *，我们试图读取的字符，
它的返回值是一个int。
这是一个有点混乱，对吗？
>> 因为我们得到一个字符，那么为什么没有这个返回一个字符？
为什么这可能不会返回一个字符，你们有什么想法？
[的大小姐答案，不知所云] >>呀。所以大小姐是完全正确的。
如果它是ASCII，那么这个整数被映射到一个实际的字符。
可能是一个ASCII字符，这是正确的。
这到底发生了什么。
我们使用的是一个int，只是因为它有更多的比特。
这是更大的不是一个字符的字符只有8位，32位机，1个字节。
和一个int的所有4个字节的空间价值。
事实证明，fgetc函数的工作方式，
如果我们向下滚动本手册页一点点，在我们的简介
向下滚动的方式。原来，他们用这种特殊的值EOF。
这是一个特殊的常量，fgetc函数的返回值
只要你打的文件，或者如果你得到一个错误。
事实证明，做这些比较EOF正确的，
你想有额外的大量的信息，你必须在一个int
而不是使用一个char变量。
即使fgetc函数有效地从一个文件中的字符，
你要记住，它返回的东西是你的类型为int。
这就是说，它很容易使用。
它给我们一个字符，所以我们要做的是保持要求的文件，
“给我的下一个字符的下一个字符给我，给我的下一个字符。”
直到我们得到的文件结束。
这会拉一个字符的时间从我们的文件，
然后我们可以做任何我们喜欢它。
我们可以将其存储，我们可以将其添加为一个字符串，我们可以把它打印出来。
任何。
>> 放大退了出去，并要回到我们的cat.c的程序，
如果我们要使用fgetc函数，
我们会如何对待这下一行代码？
我们将使用 - 弗里德会做一些事情略有不同。
而这个时候，我们只是要使用fgetc函数得到一个字符的时间。
为了处理一个完整的文件，我们可能会做什么？
在一个文件中有多少个字符？
有很多。所以，你可能希望得到一个
然后得到另一个获得，并得到另一个。
你认为我们可能要在这里使用的是什么样的算法？
什么类型的 - ？ [亚历山大]一个for循环？ “没错。
某些类型的循环。
一个for循环实际上是很大的，在这种情况下。
像你说的，这听起来像你想有一个循环，在整个文件，
获得一个字符的时间。
任何建议，可能看起来像什么？
[亚历山大，不知所云]
好了，只是英语告诉我你想要做的是什么？ [亚历山大，不知所云]
因此，在这种情况下，这听起来像我们只是试图在整个文件中环。
[亚历山大]所以，我为int的大小吗？ >>的大小 - ？
我猜大小的文件，对不对？的大小 - 我们就会把它写这样的。
尺寸文件的时间，我+ +。
因此，原来的方式，你使用fgetc，这是新的，
是，有没有简单的方法得到的文件大小
这个“大小”式的结构，你已经看到过。
当我们在使用，fgetc功能，我们要介绍的某种
新的，时髦的语法for循环，而不是只使用基本的计数器
去逐个字符，我们要拉一个字符的时候，
一次一个字符的方式，以及我们所知道的，我们是在结束
是不是当我们已经数了一定数目的字符，
但是，当我们拉出来的字符是特殊的文件结束符。
因此，我们可以做到这一点 - 我把这种路，我们要初始化
我们的第一次调用出来的文件的第一个字符。
所以这部分就在这里，这是怎么回事字符的文件
并把它存储到变量ch。
我们将继续这样做，直到我们得到的文件，
我们做测试不等于EOF字符，特殊字符。
然后，而不是做CH + +，这将只是增加价值，
因此，如果我们看到一个出来的文件，一个大写的A，说，
CH + +会给我们B，然后我们会得到C，然后按下d。
这显然​​不是我们想要的。我们想在这里
在这最后一点是，我们希望得到的下一个字符的文件。
>> 所以，我们怎么可能得到的下一个字符的文件呢？
我们怎样才能从文件的第一个字符？
[学生]：fgetfile？ >> fgetc函数，对不起，你是完全正确的。
我拼错了就在那里。所以，是的。
在这里，而不是做CH + +，
我们只是调用fgetc函数（文件）
和存储相同ch变量的结果在我们的。
[学生提问，不知所云]
>>这是这些FILE *人是特殊的。
他们的工作方式是 - 当你第一次打开 - 当你第一次作出这样的fopen的调用，
有效地作为开头的文件指针FILE *。
然后每次调用fgetc，移动一个字符通过该文件。
所以每当你打电话，你的文件增加一个字符指针。
，并时，fgetc再次，​​你要搬到另一个字符
另一个字符和其他字符和其他字符。
[学生提问，难以理解] >> that's的 - 是啊。
这是一种这种神奇的引擎盖下。
你不断递增通过。
在这一点上，你能够真正与字符。
因此，我们怎么可能打印出来的画面，现在呢？
我们可以使用相同的printf的事情，我们之前使用。
我们一直在使用一学期。
我们可以调用printf，
我们可以通过在性格就是这样。
做到这一点的另一种方式是使用printf不必做此格式字符串，而不是
我们也可以使用其他的功能之一。
我们可以使用的fputc，可打印字符在屏幕上，
除非我们看的fputc - 让我放大了一点点。
我们看到了什么是好的，是需要我们读出的字符使用fgetc函数，
但后来我们给它一个流打印到。
我们也可以用putchar函数，就可以直接输出到标准输出。
因此，有一大堆不同的选择，我们可以使用的打印。
他们都在标准I / O库。
每当你想打印 - 输出，默认情况下，将打印的特殊标准输出流，
这是标准输出。
因此，我们就可以把它作为一种本魔法值，在这里的stdout。
哎呀。将分号外部。
>> 这是很多新的，时髦的信息在这里。
很多，这是很地道的，在这个意义上，这是代码
是这样写的，只是因为它是干净的，易于阅读。
有很多不同的方式来做到这一点，您可以使用许多不同的功能，
但我们往往只是遵循相同的模式，一遍又一遍。
所以，不要惊讶，如果你看到这样的代码来了一次又一次。
好的。在这一点上，我们需要打破的一天。
感谢您的到来。感谢收看如果你在线。我们会看到你下周。
[CS50.TV]