[Powered by Google Translate] [第5条 - 更舒适]
[罗布·鲍登 - 哈佛大学]
[这是CS50。 - CS50.TV]
>> 就像我在我的电子邮件中说，有很多的东西，你可以使用
其他比设备实际做的习题集。
我们建议你这样做只是因为它在家电，那么我们就可以更容易地帮助您
我们知道如何去上班。
但是，作为一个例子，你可以做的事情，如果说，你不能够访问
的设备或您要在科学中心地下室 -
其实他们有设备 -
如果你想在任何地方工作。
一个例子是，你看到/听到的SSH？
SSH是基本上是一样的东西连接。
其实，现在我SSHed插入设备。
我从来没有直接在产品中。
这里是家电，如​​果你看这里，你看到这个IP地址。
我从来没有在设备本身;
我总是iTerm2窗口/终端窗口。
可以SSH到的IP地址，SSH jharvard@192.168.129.128。
我记得这个数字很容易，因为它是一个很好的模式。
但是，这将问我，我的密码，现在我在家电。
基本上，在这一点上，如果打开了一个终端设备本身的内部，
这个接口，但是你会使用它，是完全一样的
使用的接口，我在这里，但现在你SSHed。
你没有SSH的设备。
我敢肯定，你必须在默认情况下，另一个地方，你可以SSH是一个例子 -
哦。更大。
所有的人都应该有默认情况下，FAS FAS服务器的帐户。
对于我来说，我会SSH的rbowden@nice.fas.harvard.edu。
它会问你的第一次，和你说的话。
我的密码，只是将我的FAS密码。
所以现在，我SSHed漂亮的服务器，和我想在这里，我可以做任何事情。
很多类，你可能需要像124，有你上传的东西在这里
提交您的问题集。
但说你没有访问您的设备。
然后你就可以像在这里做的事情，它会说 -
这仅仅是我们部分的问题。
它会问你做到这一点的设备。
相反，我会做它的服务器上。
我要解压缩。
这个问题将是你已经习惯了使用的东西，例如gedit
或其他组件内。
你不会的FAS服务器上。
这一切都只是将这个文本界面。
所以，你可以任何一个，努力学习，他们有一个文本编辑器。
他们有纳米。
纳米通常是很容易使用。
您可以使用箭头，然后键入正常。
因此，并不难。
如果你想获得真正看中的，你可以使用Emacs，
我也许不应该打开了，因为我什至不知道如何关闭Emacs的。
控制X，控制C？是啊。
或者你也可以使用vim，这是我使用。
所以这些都是你的选择。
如果你不想这样做，你也可以，如果你manual.cs50.net -
哦。
在PC上，你可以使用SSH使用PuTTY
你要单独下载。
在Mac上，你可以在默认情况下使用的终端或你可以下载iTerm2，
这是一个很好的，看中的终端。
如果你去到manual.cs50.net，你会看到一个链接到记事本+ +，
这是你可以在PC上使用。
它可以让你SFTP从记事本+ +，这基本上是SSH。
这是什么让你这样做是在本地编辑您的文件，
然后，每当你想将它们保存，它会保存到nice.fas中，
然后，你可以运行它们。
在Mac上相当于将是TextWrangler。
因此，它可以让你做同样的事情。
它可以让你在本地编辑文件，将它们保存到nice.fas，
然后，你可以运行它们。
所以，如果你遇到难题没有设备，你有这些选项
还是做你的问题集。
一个问题是，你不会有CS50库
因为nice.fas默认情况下，不具备这一点。
您可以下载的CS50库 -
我不认为我需要在这一点上。
你可以下载的的CS50库和复制到nice.fas，
我认为在这一点上，我们不使用它了呢。
或者，如果我们这样做，你的时间可以被取代它
反正的CS50库中的功能的实现。
因此，不应该有那么多的限制。
就是这样。
>> 我会回去的设备，现在我们将尽一切设备。
我们的部分问题，在开始的时候，就像我在我的电子邮件中说，
我们要谈一个短你应该看。
我们有重定向和管道这三个问题。
>> 在这流功能，如输出默认情况下写的？
所以流。什么是流？
甲流是基本上一样，只是一些 -
它甚至不是一个源1s和0s。
这里要求的是标准输出流。
因此，标准输出流，当你写它，
它出现在屏幕上。
标准输出流，这意味着你只写1“和”0“到它，
标准输出的另一端从该流中读取数据。
这只是一个字符串1s和0s。
你可以写流，或者你可以从数据流中读取
根据流实际上是什么。
其他两个默认流的标准和标准错误。
标准的是每当你的GetString，它正在等待你输入的东西。
因此，它在等着你，它实际上是在等待标准，
这是真的，你会得到什么，当你在键盘上输入。
你输入标准英寸
标准错误是基本上等同于标准输出，
但它的专业，当您打印到标准错误，
你应该只打印错误消息，该
这样你就可以区分常规消息打印到屏幕上
对误差取决于他们是否去到标准输出和标准错误的消息。
文件了。
标准输出，标准和标准错误是特别的流，
但实际上任何文件，当你打开一个文件时，它变成一个字节流
在那里你可以从该流中读取。
您，在大多数情况下，只是觉得一个文件的字节流。
所以他们写什么流在默认情况下？标准输出。
>> >和>>之间的区别是什么？
没有人事先观看视频？好吧。
>将是如何重定向到文件，
>>也将输出重定向到文件中，
但它的，而不是要追加到文件。
例如，让我们说，我正好有字典在这里，
唯一的字典里面的东西是猫，猫，狗，鱼，狗。
一个命令，你必须在命令行是猫，
只是要打印在一个文件中。
所以当我说猫字典，它要打印的猫，猫，狗，鱼，狗。这是所有的猫做。
这意味着，猫，猫，狗，鱼，狗，它打印到标准输出。
如果我不是要重定向到一个文件中，我可以使用>将它重定向到任何该文件是。
我会打电话给该文件的文件。
所以，现在如果我LS，我会看到我有一个新的文件名为文件。
如果我打开它，它将会有什么猫放在命令行。
所以，现在如果我这样做了，那么它的输出重定向到文件，
和我有同样的事情。
所以，从技术上来说，它完全推翻了我们什么。
我们会看到，如果我改变了字典，我拿出狗。
现在，如果我们猫词典到文件中，我们将有新的版本与狗删除。
因此，它完全覆盖。
相反，如果我们使用“>>”，这是怎么回事追加文件。
现在，打开文件，我们可以看到，我们有同样的事情两次印刷
有一次，因为它是，那么我们就追加到原来的。
所以，这就是>和>>。
请问下一个要求 - 它不要求它。
>> 我们有另一种是<，如果标准输出重定向，
<将被重定向标准英寸
让我们来看看，如果我们有一个例子。
我可以写一个真正的快速。
让我们任何文件hello.c。
相对直截了当文件。
我只是一个字符串，然后打印“Hello”无论我刚刚输入的字符串是。
因此，请hello，然后。/个招呼。
现在，它提示我输入的东西，
这意味着它等待的东西，将予订立标准英寸
因此，进入任何我想要的标准。我们要好好说“你好”，罗布！
然后打印到标准输出您好，来抢！
如果我这样做。/ hello，然后重定向，
现在你可以只从一个文件重定向。
所以，如果我把一些文件，TXT，我把罗布，
如果我跑个招呼，然后重定向到的文件文本。/打招呼，它会说“你好”，抢！立即。
当它第一次得到的GetString和它的等待标准，
标准中不再等待键盘上的数据，从而得到进入。
相反，我们从文件中读取TXT重定向标准。
因此，它会读取TXT的文件，这仅仅是行抢，
那么它会打印Hello，罗布！
如果我想，我也可以做。/你好TXT
然后指出它的标准印刷，您好，这是来抢！
我可以重定向到它自己的文件。
我只需要调用的文件招呼 - 不，我不会，因为这是可执行文件 - txt2。
现在，，txt2是要输出/你好<txt文件，这将是您好，罗布！
>> 有问题吗？
>> 好吧。那么在这里，我们有管道。
管道的最后一个单元的重定向。
>> 哦。我想一个单位的重定向，如果不是你2>，
重定向标准错误。
因此，如果发生了标准错误，它会不会被投入txt2。
但是请注意，如果我这样做2>，然后它仍然印刷您好，来抢！在命令行
因为我只重定向标准错误，我不重定向标准输出。
标准错误和标准输出是不同的。
如果你想真正写入到标准错误，
然后我可以改变这是fprintf到stderr。
所以，默认情况下，打印输出到标准输出。
如果我想手动打印到标准错误，那么我必须使用fprintf
并指定我要打印到的。
相反，如果我做了fprintf标准输出，那么这基本上等同于printf的。
，但fprintf标准错误。
所以，现在，如果我重定向到txt2，您好，来抢！还是会被印在命令行
，因为它的打印到标准错误，我只重定向标准输出。
如果我现在将标准错误重定向，现在也没有打印出来，和txt2将是你好，抢！
那么现在，你可以打印你的实际标准错误的错误
打印您的的常规邮件到标准输出。
所以当你运行你的程序，你可以运行它。/您好，这类型的2>
让你的程序将正常运行，
但任何错误消息，让你可以检查你的错误日志中，
这样的错误，然后看后，你的错误，文件中有任何错误发生。
>> 有问题吗？
>> 最后一个是管道，你能想到的，所承担的标准，从一个命令
和它的标准中的下一个命令。
这里是一个例子，echo是一个命令行的东西
这只是呼应，我把什么作为它的参数。我不会把引号。
回音胡说，胡说，胡说，只是要打印等等，等等，等等。
在此之前，当我说，我只好把罗布到一个txt文件
因为我只能重定向txt文件，而不是，/如果我呼应罗布
然后通过管道到/个招呼，这也将做同样类型的东西。
这是此命令的输出，回声罗布，
并用它作为输入/个招呼。
你可以认为它是第一个到一个文件重定向回声罗布
，然后输入/你好，文件只是输出。
但它需要的临时文件的图片。
>> 该问题吗？
>> 接下来的问题是会涉及到这一点。
管道，你可以用它来寻找一个称为names.txt的文件的唯一名称是什么？
，我们将要在这里使用的命令是唯一的，因此uniq中，然后厕所。
你可以做男人的uniq的实际看是什么做的，
它只是将筛选相邻的匹配行从输入。
男子厕所是要打印的换行，字和字节计数的每个文件。
我们将要使用的是最后一个排序，
这是怎么回事，只是txt文件的行进行排序。
如果我做一些txt文件，的names.txt，它的罗布，张宇，约瑟夫，张宇，约瑟夫，RJ，罗布，
我想在这里做什么的，是在这个文件中找到的唯一名称。
那么应该怎样的答案呢？ >> [学生] 4。 >>呀。
它应该是4，因为罗布，汤米，约瑟夫，RJ是在这个文件中只有唯一的名称。
第一步，如果我只是做的字数的names.txt，
这实际上是告诉了我一切。
其实，这是印刷 - 让我们来看看，男人WC - 换行，单词和字节计数。
如果我只在乎线，然后我就可以做WC-L names.txt。
所以这是第1步。
但我不希望到厕所 - 1 names.txt，因为names.txt只包含了所有的名字，
我要过滤掉任何非唯一的。
所以，如果我做的uniq names.txt，不很给我我想要的
因为重复的名称仍然存在。
这是为什么？ uniq的是为什么不这样做我想要什么？
[学生]重复[听不清] >>呀。
记住的uniq手册页说，过滤器相邻的匹配行。
他们是不相邻的，所以它不会过滤。
如果我先对它们进行排序，排序names.txt是打算把所有重复的行。
所以，现在排序names.txt是。
我将要使用的uniq的，这是| uniq的输入。
这给了我约瑟夫，RJ，抢夺，汤米，
我想使用这个输入到wc-L，
这是要给我4。
喜欢它说，在这里，你可以使用什么样的管道？
你可以做很多的事情，比如使用一系列的命令
在您使用一个命令的输出作为下一个命令的输入。
你可以做很多事情，很多聪明的事情。
>> 有问题吗？
好吧。
这是它的管道和重定向。
>> 现在我们的实际的东西，编码的东西。
本PDF里面，你会看到这个命令，
你要运行此命令在您的设备。
wget是命令只是从互联网上得到的东西，基本上，
所以wget和URL。
如果你到这个URL在浏览器中，它会下载该文件。
我只是点击就可以了，所以对我来说，它下载的文件。
但是，写的那个东西的wget内的终端
只是要下载到您的终端。
我有section5.zip的，和你要解压section5.zip的，
这是给你一个文件夹，名为第5章，
这是我们要使用它里面的所有的文件。
随着这些项目的文件名称所暗示的，他们是一个有点缺陷，
所以，你的任务是要弄清楚为什么使用gdb。
每个人都有他们下载/知道如何让他们下载
设备到他们的吗？好吧。
>> 运行./buggy1，它会说分割故障（核心转储）
任何时候你得到一个segfault，这是一个不好的事情。
在什么情况下，你得到一个segfault？
[学生]：取消引用一个空指针。 >>呀。所以这是一个例子。
解引用一个空指针，你会得到一个segfault。
一个segfault手段是你感动的回忆，你不应该接触。
因此，释放空指针动人的地址为0，
基本上，现在所有的电脑说，地址为0的内存，你不应该接触。
所以这就是为什么一个空指针解引用一个segfault。
当你的事发生在初始化的指针，那么它有一个垃圾值，
因此，当您尝试取消引用它，在所有的可能性你感动的回忆
这是在中间的地方。
如果你碰巧很幸运，垃圾的价值
发生点在堆栈上或东西的地方，
那么当你解引用指针，你还没有初始化，
什么都不会出问题。
但是，如果它指向的栈和堆之间的某个地方，说，
它指向只是到别的地方，没有被使用你的程序，
那么你感动的回忆，你不应该接触和你的段错误。
当你写一个递归函数，递归太多次
堆栈的增长过大，堆栈碰撞到的东西
它不应该被碰撞，你感动的回忆，你不应该接触，
因此你段错误。
这是一个segfault。
>> 这是同样的道理，如果你有这样的字符串 -
让我们回到前面的程序。
在hello.c的，我只是要别的东西。
的char * s =“世界你好！”;
如果我使用的东西或S * S = [0] ='X';
所以一定要打招呼，/你好，为什么是段错误？
为什么这个段错误呢？
你会想到什么事情发生呢？
如果我做了printf的（“％s \ n”）;你会想到要打印的吗？
[学生] X打招呼。 >>呀。
现在的问题是，当你声明一个这样的字符串，
s是一个指针，要在栈上，
什么s指向的是这个字符串包含在只读存储器。
因此，只要名称，只读存储器，你应该得到的想法
如果你试图改变什么只读存储器，
你正在做的事情你不应该做的内存和段错误。
这实际上是一个很大的区别的char *和char []。
所以个char []，现在这个字符串将被放在堆栈中，
且堆栈无法读取，这意味着，这应该工作完全正常。
它。
请记住，当我这样做的char * s =“世界你好！”，S本身是在栈上
但s指向其他地方，其他地方恰好是只读。
但个char []是在栈上的东西。
所以这是一个segfault发生的另一个例子。
>> ，我们看到./buggy1导致一个segfault。
从理论上讲，你不应该看buggy1.c立即。
相反，我们将看看它通过gdb的。
请注意，当你得到分割故障（核心转储），
你得到这个文件，这里所说的核心。
如果我们的ls-l，我们可以看到，核心通常是一个相当大的文件。
这是该文件的字节数，所以它看起来像它的250岁的千字节为单位。
这样做的原因是什么实际上是核心转储
是当你的程序崩溃时，你的程序的内存状态
刚刚被复制，并粘贴到该文件中。
它被倾倒到该文件中。
这个程序，运行时，正巧有一个约250千字节的内存使用情况，
所以这是什么得到了倾入这个文件。
现在，你可以看一下该文件，如果我们做GDB buggy1核心。
我们可以做gdb的buggy1，将只启动gdb的定期，
使用buggy1作为它的输入文件。
但是，如果你GDB buggy1的核心，那么它明确要启动GDB
看，核心文件。
你说buggy1手段gdb的都知道，该核心文件来从buggy1程序，。
GDB buggy1核心是要立即给我们带来
的地方发生的程序终止。
我们在这里看到的信号，分割故障终止程序。
我们碰巧看到的组装线，这可能是非常有帮助的。
但是，如果你键入BT或回溯，这将是功能
这样，就为我们提供了我们当前的堆栈帧的列表。
所以回溯。它看起来像我们只有两个堆栈帧。
首先是我们的主要的堆栈帧，
第二个是，我们正好是在这个函数的堆栈帧，
看起来我们只需要的汇编代码。
所以，让我们回到我们的主要功能，
做到这一点，我们可以做的第1帧，我觉得我们也可以做下来，
但我几乎从来没有办下来 - 或上升。是啊。
向上和向下。最多给你带来一个堆栈帧，同比下降给你带来了一个堆栈帧。
我倾向于从来没有使用它。
我只是具体说，这是去的帧标记为1的第1帧。
第1帧，将要带给我们的到主堆栈帧，
在这里，它说发生在我们的代码行。
如果我们想要一对夫妇更行代码列表，我们可以说，
而这会给我们周围所有的代码行。
我们segfaulted该生产线为6：
（STRCMP（“CS50石头”，ARGV [1]）== 0）。
如果没有明显的是，你可以得到它直接从这里只是想，为什么segfaulted。
但是，我们可以把它一步，说，“我为什么要ARGV [1]段错误呢？”
让我们打印的argv [1]，它看起来像它的0x0，这是空指针。
我们strcmping CS50石块和空，所以会出现段错误。
又为什么是argv [1]为空？
[学生]：因为我们没有给它任何命令行参数。
是啊。我们没有给它任何命令行参数。
所以./buggy1的argv [0]是./buggy1。
它不会有一个argv [1]，因此，会出现段错误。
但是，如果不是，我做的只是CS50，它会说你得到D
，因为这是它应该做的。
在buggy1.c看，它应该打印“你得到一个D” -
如果argv [1]，而不是“CS50岩石”，“你得到了D”，否则，你会得到一个A！“
因此，如果我们要一个一个，我们需要的比较结果为真，
这意味着，它比较为0。
的argv [1]因此，需要将“CS50岩石”。
如果你在命令行上要做到这一点，你需要使用\逃跑的空间。
因此，CS50 \岩石，你会得到一个A！
如果你不这样做的反斜杠，为什么不工作？
[学生]：这是两个不同的参数。 >>呀。
的argv [1]将是CS50，和argv [2]将是岩石。好吧。
>> 现在./buggy2是再次向段错误。
，其核心文件，而不用打开它，我们就打开了buggy2直接，
所以GDB buggy2。
现在，如果我们运行我们的程序，那么它会说程序收到信号SIGSEGV，
这是段错误的信号，这是它发生在发生。
我们回溯看，我们看到我们在功能oh_no，
这是所谓的由函数极小，这是所谓的由函数binky，
被称为主。
我们也可以看到这些功能的参数。
极小和宾基的参数为1。
如果我们列出的功能oh_no，我们看到，oh_no，只是做的char * s = NULL;
* s =“BOOM”;
为什么会失败呢？
[学生]无法取消引用空指针？ >>呀。
这只是说s是NULL，无论如果出现这种情况，是一个char **，
这取决于你如何解释它，它可能是一个指向一个指针，指向一个字符串的指针
或一个字符串数组。
s为null，* S是一个空指针解引用，
所以这是要崩溃。
这是一个最快捷的方法，你可以可能出现段错误。
这只是宣布一个空指针，并立即段错误。
这是什么oh_no。
如果我们去了一个框架，然后，我们将要进入​​的功能，称为oh_no。
我需要做下来。
如果你不输入命令，你只要再次按下Enter键，
它只是重复前面的命令，你跑了。
我们在第1帧。
上市这一框架内，我们在这里看到的是我们的功能。
你可以打列表，或者你可以做的清单20，它会列出。
该函数的极小的说，如果我是1，然后去的oh_no功能，
其他紧身的功能。
我们知道，我是1，因为我们在这里碰巧看到
被称为参数1，极小的。
或者你也可以不打印我，它会说我是1。
目前，我们正在极小，如果我们去了另一个框架，我们知道我们将最终在宾基。
最多。现在，我们在binky。
列出这个功能 - 在上半场结束前把我的清单 -
它开始了，如果我是0，然后，我们将调用它oh_no，否则打电话极小。
我们知道，我是1，因此它被称为极小的。
，现在我们在主，主要是int i =兰特（）％3;
这仅仅是给你一个随机数是0，1或2。
这将调用binky与数字，它会返回0。
见到这种情景，
步行通过的程序不运行它立即，手动
在主，你可以设置一个破发点，这意味着当我们运行程序
你的程序运行，直到它击中了一个破发点。
因此，运行程序，它会运行，然后它会为主打功能，并停止运行。
现在，我们的主要内，步骤或未来将会给我们带来的下一行代码。
你可以做的步骤或未来。
单击Next，现在我已经设置的rand（）％3，这样我们就可以打印i的值，
它会说我是1。
现在它无论我们使用下一个或步骤。
我想它在上一个重要的，但我们希望下次使用。
如果我们使用步骤，我们进入的功能，这意味着在实际的东西看
内发生的事情的binky。
如果我们用下，那么就意味着在功能
只是去我们的主函数中的代码的下一行。
在这条线就在这里，我是在它说的rand（）％3;
如果我这样做的一步，将进入实施兰特
并期待在那里发生了什么事，我可以通过rand函数的步骤。
但我不关心rand函数。
我只想去到下一行代码的主，所以我用下。
但我现在关心的binky功能，所以我想步入。
现在，我在binky。
的第一行代码是怎么说的（我== 0），我走了一步，
我们可以看到，我们结束了在极小的。
如果我们列出的东西，我们可以看到，它检查是i = 0。
我是不等于0，所以去的其他条件，
要调用极小的（I）。
你可能会感到困惑。
如果你只是直接看这些线，你可能会认为，如果（我== 0）
好，那我走了一步，现在我在极小的（I），
你可能会认为一定意味着i = 0或东西。
它只是意味着它知道它可以直接粘到线极小的（I）。
由于i是不为0时，下一个步骤是不会结束的else。
Else是不是要停在一条线。
它只是要去到下一行，它实际上可以执行，这是极小的（I）。
步入极小的（I），我们可以看到，如果（我== 1）。
我们知道I = 1，所以当我们一步，我们知道我们要在oh_no
因为I = 1调用函数oh_no，你可以进入，
这是要设置的char * s = NULL，并立即“BOOM”。
然后寻找在实施buggy2，
这一点，我只是得到一个随机数 - 0，1或2 - 呼叫宾基，
如果我是0它要求oh_no的，否则它会调用极小，来了这里。
如果i是1，呼叫oh_no，否则打电话紧身，这即将在这里，
如果是2，调用oh_no。
我什至不认为有一种方法 -
有没有人看到这是一个程序，不会出现段错误的一种方式吗？
因为除非我失去了一些东西，如果我是0，你马上就会出现段错误，
否则你去一个函数，如果我是你段错误，
否则，你去给一个函数，如果我是2段错误。
所以无论你做什么，你段错误。
>> 我想修复它，而不是做的char * s = NULL的方式之一，
你可以MALLOC该字符串的空间。
我们可以做的malloc（大小） - 大小是什么？
[学生]（字符）* 5？ “这似乎不是吗？
我假定这将工作，如果我真的跑了，但它不是我在寻找什么。
看看s的类型。让我们增加对int *，所以int * X。
我会做的malloc（sizeof（int）的的）。
或者，如果我想要一个数组5，我会做（如sizeof（int）* 5）;
如果我有一个int **吗？
那我的malloc吗？
[学生]大小的指针。 >>呀。 （如sizeof（int *））;
同样的事情在这里。
我想（如sizeof（char *）的）;
这是怎么回事“轰”的指针，该指针指向分配空间。
我并不需要分配空间的“BOOM”
因为这基本上是我在说什么之前
对char * x =“BOOM”。
“BOOM”已经存在。它发生在存在内存中的只读区域。
但它已经存在，这意味着这行代码，如果s是一个char **，
* S是一个char *，你设置这个char *到指向“BOOM”。
如果我想复制到s的“BOOM”，那我就需要为s分配空间。
* S =我会做的malloc（sizeof（字符）* 5）的;
为什么？
为什么不是4？它看起来像“BOOM”是4个字符。 >> [学生] NULL字符。
是啊。需要空字符的字符串。
现在，我可以做类似的strcat - 复制一个字符串的功能是什么？
[学生]京华山一？ “STRCPY。
男人的strcpy。
因此，STRCPY或strncpy（）函数。
strncpy（）函数是一个比较安全的，因为你可以指定到底有多少个字符，
但在这里并不重要，因为我们知道。
因此，strcpy和寻找的参数。
第一个参数是我们的目标。
第二个参数是我们的源代码。
我们要复制到我们的目的地* S的指针“BOOM”。
你为什么要做到这一点，而不是正是我们之前用的strcpy
* S =“BOOM”？
是有原因的，你可能想这样做，但是那是什么原因呢？
[学生]如果你想改变一些东西在“BOOM”。 >>呀。
现在，我可以做一些事情，比如s [0] ='X';
，因为点的堆的堆，空间，s是指向
是一个指针，更在堆上的空间，这是存储“BOOM”。
因此，这“轰”的副本被存储在堆中。
“BOOM”在我们的计划在技术上是有两个副本。
这只是这个“BOOM”字符串常量的第一个，
和“嘭”的第二个副本，，STRCPY创建“嘭”的副本。
但是，“嘭”的副本存储在堆中，堆，你是自由的改变。
堆是只读的，因此这意味着，S [0]
是打算让你改变了价值的“BOOM”。
这将让你改变这些字符。
>> 有问题吗？
好吧。
>> 到buggy3，让GDB buggy3。
我们只要运行它，我们可以看到，我们得到一个segfault。
如果我们回溯，有只有两个函数。
如果我们进入到我们的主函数中，我们看到，我们segfaulted在这条线。
因此，只要在这条线，（线= 0; FGETS这东西不等于NULL;
线+ +）。
我们的前一帧被称为_IO_fgets的。
你会看到有很多内置的C函数，
当你得到段错误，会有非常神秘的功能名称
像这个_IO_fgets。
但是，这是怎么回事涉及到这FGETS通话。
内的某处在这里，我们是段错误。
如果我们对fgets的观点看，我们可以打印缓冲区。
让我们打印 - 哦，不。
打印不完全一样，我希望它去上班。
让我们来看看在实际的程序。
缓冲区是一个字符数组。这是一个128个字符的字符数组。
所以，当我说，打印缓冲区，它要打印的这128个字符，
我的猜测是期望是什么。
我一直在寻找的是打印缓冲区的地址，
但是，这并不真正告诉了我很多。
所以，当我碰巧在这里说×缓冲液，它让我看到0xbffff090，
其中，如果你还记得从早期的一些点，Oxbffff往往是一个堆栈上下的区域。
该协议栈往往只是根据0xc000开始的地方。
看到这个地址，我知道该缓冲区在堆栈上正在发生的事情。
重新启动我的程序运行起来，缓冲，我们看到的是这个字符序列
几乎是毫无意义的。
然后打印文件，这是什么文件是什么样子？
[学生] NULL。 >>呀。
文件是一个的类型FILE *，所以它是一个指针，
该指针的值是空的。
所以FGETS会尝试读取该指针以间接的方式，
但为了访问该指针，它有取消对它的引用。
或者，为了访问它应该被人指指点点，解引用它。
因此，它释放空指针，它的segfaults。
我可以重新启动它。
如果我们打破我们的重点和运行，
第一行代码是char *文件名=“nonexistent.txt”;
这应该给一个相当大的提示，这个计划失败的原因。
键入下，我到下一行，我打开这个文件，
然后我立刻进入我们的产品线，其中一次我打了未来，它会出现段错误。
有没有人想抛出一个原因，我们可能会段错误？
[学生]文件不存在。 >>呀。
这应该是一个提示
每当你打开一个文件时，你需要检查文件是否存在。
所以在这里，“nonexistent.txt”;
当我们阅读FOPEN文件名，我们就需要说
（文件== NULL）说printf（“请文件不存在！”
- 更好 - 文件名）;返回1;
所以，现在我们要检查是否为NULL
之前，实际上继续，并试图读取该文件。
我们可以重新创建它，只是看到这样的作品。
我打算包括一个新的生产线。
所以，现在nonexistent.txt不存在。
这样的事情，您应经常检查。
您应经常检查，看看FOPEN返回NULL。
您应经常检查，以确定的malloc不返回NULL，
否则，你段错误。
>> 现在buggy4.c。
运行。我猜这是等待输入或可能无限循环。
是的，它是无限循环。
所以buggy4。它看起来像我们的无限循环。
我们可以突破，主要运行我们的程序。
在gdb，只要你使用的缩写是明确的
或者，他们会为您提供特别的缩写，
那么你就可以使用n下次使用，而不必输入下所有的方式。
现在，我已经打了N一次，我可以只按下回车键继续下
而不是打n输入n输入，n输入。
它看起来像我在某种循环的设置阵列[i]为0。
它看起来像我从来没有打破这循环中。
如果我打印我，所以我是2的话，我会去下。
我要打印我，我是3，然后我会去下。
我要打印我和我的3。下一步，打印我，我是4。
实际上，打印sizeof（阵列）的，所以阵列的大小是20。
但它看起来像有一些特殊的gdb命令下去，直到事情发生。
这就像设置条件变量的值。但我不记得它是什么。
因此，如果我们继续下去 -
你刚才说什么？你带来了什么呢？
[学生]不显示我添加 - “”是啊。因此，显示我可以帮忙。
如果我们只是显示我会把这里i的值是什么
所以我没有把它打印出来，每次。
如果我们只是继​​续下一个，我们看到0，1，2，3，4，5，0，1，2，3，4，5，0，1，2，3，4，5。
有些事情正在发生可怕的错误，而我正在被重置为0。
在buggy4.c，我们看到的情况是int数组[5];
（i = 0; <= sizeof（数组）的，我+ +）
阵列[i] = 0;
这是错在这里，我们看到了什么？
作为一个提示，当我在做gdb的buggy4 - 让我们将主要的运行 -
我也只看到打印sizeof（数组）的条件是什么，我终于爆发。
我在哪里？我跑了吗？
我没有申报。
因此，打印大小（数组）和20，
这是意料之中的，因为我的数组的大小是5，它是由5个整数，
所以，整个事情应该是5 * sizeof（int）的的字节，其中的sizeof（int）往往是4。
因此，大小（阵列）是20。
应该是什么内容呢？
[学生]将是sizeof（int）。 “是啊，/是sizeof（int）。
它看起来像有一个问题在这里。我想这应该是<
因为它几乎总是<永不<=。
现在，让我们来想想这是为什么居然断。
没有任何人有猜测为什么我重置为0到每个迭代循环吗？
内这里发生的事情是，唯一阵列[i]被设置为0。
不知何故，这行代码导致我们的诠释，我将其设置为0。
[学生]难道是我这部分的记忆，因为它的首要
当它认为它的下一个元素的数组？ >> [鲍登]是的。
当我们超越我们的阵列，
不知何故，我们覆盖的空间，是压倒一切的我的价值。
所以，如果我们看一下为buggy4，打破主体，运行，
让我们打印i的地址。
看起来就像是bffff124。
现在，让我们打印的地址的数组[0]。 110。
怎么样[1]？ 114。
[2]，118。
11c中，120。阵列[5] bfff124。
因此，阵列[5]，因为我，这意味着该数组[5]我有相同的地址。
如果他们有相同的地址，他们是同样的事情。
所以，当我们阵列[5]为0，我们正在设置i为0。
如果您认为有关这方面的堆栈，
诠释我首先声明，这意味着我得到了一些堆栈空间。
阵列[5]，20个字节被分配在栈上分配。
所以，我首先被分配，那么这20个字节被分配。
所以，我前阵发生，
因为，就像我上周表示，在技术堆栈向下增长的，
当你对数组的索引，我们也能保证这个数组中的第0个位置
总是发生之前，数组中的第一个位置。
这是我画了上周的种。
请注意，在底部我们的地址为0，并在顶部，我们有地址最大。
该协议栈始终是越来越大了下来。
比方说，我们分配给我。
我们分配整数i，这意味着我们只能说，这里整数，我被分配。
然后我们分配了5个整数组成的数组，这意味着底下的，
由于堆栈的增长，这5个整数如何分配。
但是，由于阵列是如何工作的，我们保证在数组中的第一个位置
总是有的地址小于数组中的第二件事情。
因此，数组中的位置始终为0，首先发生在内存中，
而数组中的位置发生后，
数组中的位置2具有发生之后，
这意味着，数组中的位置0将发生的地方，在这里，
数组中的位置会发生以上，
因为移动意味着更高的地址以来的最高地址是在这里。
所以数组[0]在这里，数组[1]起来这里，阵列[2]起来这里，阵列[3]在这里。
请注意之前，我们如何分配的整数，我在这里，
随着我们进一步到我们的数组，我们正在越来越接近我们的整数i。
碰巧的是该数组[5]，这是超出了我们的数组中的一个位置，
正是在那里我正好是整数分配。
所以，这点，我们碰巧被击中的堆栈空间
被分配的整数i，我们设置为0。
>> 这是如何工作的。有问题吗？是啊。
[学生]不要介意。好吧。
[学生]：你如何避免这些类型的错误呢？
这些排序的错误？不要使用C语言作为编程语言。
使用一种语言，数组边界检查。
只要你小心，你只需要你的数组的边界，以避免过去。
[学生]所以，在这里，当我们走过你的数组的边界 -
鲍登这就是事情开始出错。 >> [学生]：哦，好吧。
只要你留在分配的内存为您的阵列，你的罚款。
但是C没有错误检查。如果我这样做阵列[1000]，它会很高兴地只需要修改无论发生什么事 -
它进入阵列的开头，然后它进入1000位置后，将其设置为0。
它没有做任何检查，哦，这不居然有1000的东西在里面。
1000是远远超出我应该改变什么，
而Java或什么的，你会得到数组的界限指标
或指数范围异常。
这就是为什么有很多的高级语言有这些东西
如果你超越了数组界限，你失败了
所以，你不能改变的东西从下面你
事情就远不如刚刚得到一个异常
他说，你去结束之后的数组。
[学生]因此，我们应该只是改变了<=只是<？ >> [鲍登]是啊。
它应该是“大小（数组）/ sizeof（廉政）;
大小（数组）是20，但我们只希望5。 >> [学生]。
还有问题吗？好吧。
>> [学生]：我有一个问题。 >>呀。
[学生]：什么是实际的数组变量？
[鲍登]究竟什么是数组吗？
数组本身是一个符号。
这仅仅是我们所引用的20个字节的开始地址。
你可以认为它是一个指针，但它是一个常量指针。
只要东西被编译，变量数组不存在了。
[学生]那么，它是如何找到数组的大小吗？
数组的大小是指，该块的大小，符号是指。
当我做的东西像printf（“％p \ n”，阵列）;
让我们来运行它。
我刚刚做了什么错了吗？
阵列的阵列“在这里声明。
哦，在这里。
铛是聪明的，和它发生，请注意，我宣布5个元素的数组作为
但我索引到位置1000。
它可以这样做，因为这些都只是常量。
它只能走这么远，我注意到，超出了数组界限。
但是请注意，当我们有我是不正确的，
，它可能无法确定我能承担多少值，
所以也不能确定，我打算结束之后的数组。
这只是铛聪明的。
>> 但现在buggy4。那么，还有什么我做错了什么？
库函数printf的'隐式声明。
我想＃包括的。
好吧。现在运行buggy4。
打印值的数组像我在这里做的，打印出来作为一个指针
打印出一些东西，看起来像这样 - bfb8805c - 这是一些地址
这是在堆栈上下的区域。
数组本身是这样一个指针，但它不是一个实际的指针，
因为常规的指针，我们可以改变的。
阵列仅仅是一些常数。 20块的开始在地址0xbfb8805c的内存。
所以bfb8805c通过这个地址+20或我猜-20 -
这个数组分配的内存。
数组，变量本身不存储任何地方。
当你在编译时，编译器 - 手一挥 -
但编译器将只使用它知道数组是。
它知道该数组的开始，
，因此它可以做的事情在一定的偏移量从这个开始。
它并不需要一个变量本身代表阵列。
但是，当我做一些事情，如int * p =阵列，p是一个指针，它指向该数组，
现在p实际上不存在在栈上。
我将p。我可以做p = malloc的。
因此，它最初指向阵列，现在它指向的堆一些空间。
我不能这样做阵列= malloc的。
如果铛是聪明的，它会冲我喊了蝙蝠的权利。
其实，我敢肯定，海湾合作委员会将做到这一点。
因此，数组类型为int [5]“是不可转让的。
您无法分配到一个数组类型的东西
因为数组是一个常数。
这是一个符号引用这20个字节。我不能改变它。
>> [学生]数组的大小是存储在哪里？
鲍登这不是存储在任何位置。这时候，它的编译。
那么，是存储数组的大小？
您可以使用sizeof（数组）的函数，数组声明本身的内部。
所以，如果我做了一些功能，foo和我这样做（int数组[]）
输出（“％d \ n”，sizeof（数组）的）;
然后，我在这里调用foo（数组）;
里面的这个功能 - 让我们来运行它。
这是再聪明的铿锵。
它告诉我的sizeof数组函数参数
将返回了'int *'的大小。
这将是一个错误，如果它不是我希望发生的。
关闭Werror。
警告。警告罚款。
它仍然会编译，只要它有一个警告。
。/ a.out的打印4。
生成的警告，是一个明确的指标，出了什么问题。
int数组只是要打印的大小（*）。
即使我把阵列[5]在这里，它仍然只是要打印的大小（*）。
因此，只要你把它传递到一个函数，数组和指针的区别
是不存在的。
这恰好是一个数组，在堆栈上声明，
但只要我们把这个值，0xbf胡说，胡说，胡说到这个函数，
那么这个指针指向该数组在堆栈中。
因此，这意味着，大小只适用于被宣布的阵列的功能，
这意味着，当你在编译此功能，
铛“时，通过这个功能，它认为数组是一个int数组大小为5。
那么它看到的大小（数组）。嗯，这是20。
这实际上是大小基本上几乎所有的情况下。
sizeof是一个函数，它是一个运营商。
你不调用sizeof函数。
如sizeof（int），编译器会翻译到4。
明白了吗？好吧。
>> [学生]那么，什么是大小（阵列）之间的区别主要在富？
这是因为我们说，这是int *类型的sizeof（数组）的，
而数组在这里是不是int *类型，它是一个int数组。
>> [学生]所以，如果你有参数的数组[]，而不是int *数组，
这意味着，你仍然可以改变，因为现在它是一个指针数组吗？
鲍登]像这样吗？ >> [学生]是啊。你可以改变数组内的功能呢？
鲍登在这两种情况下，你可以改变阵列。
在这两种情况下，你是说数组[4] = 0。
[学生]但你可以使阵列点别的东西吗？
鲍登哦。是啊。在这两种情况下 - >> [学生]是啊。
鲍登]数组[]和int *数组之间的区别，是没有的。
在这里，您还可以得到一些多维数组
一些方便的语法，但它仍然只是一个指针。
这意味着，我可以自由地做阵列的malloc（如sizeof（int））;现在指向别的地方。
但就像是如何工作的，永远总是，
改变这种阵列，使其指向其他的东西
不改变这个数组，在这里，因为它是一个副本的说法，
它不是一个指针，这样的说法。
而实际上，随着越来越多的迹象表明，它是完全一样的 -
我们已经看到了印刷阵列打印 -
如果我们打印的地址数组的数组的地址或地址
无论是那些？
让我们忽略这一项。
好吧。这是罚款。现在，运行。/ a.out的。
印刷阵列，然后打印该数组的地址，同样的事情。
阵列根本就不存在。
它知道，当你打印数组，你要打印的符号，指的是那些20个字节。
好，打印的地址的数组，数组的操作是不存在的。
它不会有一个地址，所以它只是打印这20个字节的地址。
只要你编译，想在你的编译buggy4。/ a.out格式，
数组是不存在的。
指针存在。数组没有。
代表该数组的内存块仍然存在，
但该类型的变量数组和变量不存在。
这些都是像数组和指针之间的主要区别
只要你调用函数，没有任何区别。
但里面被声明为数组本身的功能，大小不同的方式工作
因为你打印的类型的大小，而不是块的大小，
你不能改变它，因为它是一个符号。
打印的东西打印的东西和地址同样的事情。
而这几乎是它。
[学生]你能说更多的时间吗？
我可能错过了一些东西。
印刷的数组的数组和地址打印同样的事情，
而如果你打印的指针与指针​​的地址，
打印一件事你指向的地址，
其他打印的指针在栈上的地址。
你可以改变一个指针，你不能改变数组的象征。
和sizeof指针要打印该指针类型的大小。
所以int * p的大小（P），打印4张，
但int数组[5]打印sizeof（数组）的打印20。
[学生] int数组[5]将打印20？ “是的。
这就是为什么内的buggy4的时候，它使用的是大小（阵列）
这是我做<20，这是不是我们想要的。
我们希望I <5。 >> [学生]好吧。
[鲍登然后只要你开始传递的功能，
如果我们这样做* P =阵列;
这个函数里面，我们基本上可以使用p和阵列完全相同的方式，
除了的sizeof问题和不断变化的问题​​。
但是p [0] = 1;说阵列[0] = 1是相同的;
只要我们说富（数组）或者foo（P）;
里面的函数foo，这是两次相同的呼叫。
这两个调用之间没有任何区别。
>> 每个人都好？好吧。
我们有10分钟。
>> 我们将尝试拿到通过这个黑客打字员程序，
本网站，去年或什么的就出来了。
这只是应该像你输入随机它打印出来 -
无论它发生在已加载的文件是什么，它​​看起来像你打字。
它看起来像某种操作系统代码。
这就是我们要实现的。
你应该有一个二进制可执行文件hacker_typer
是发生在一个单一的参数，“黑客类型的文件。”
运行可执行程序，应清除屏幕
然后打印出传入的文件，每次用户按下一个键一个字符。
所以，你按什么键，它应该扔掉，而不是从文件打印字符
这是参数。
我几乎会告诉你，我们需要知道的事情是什么。
但是，我们要检查的termios库。
在我的整个生活，我从来没有使用这个库，所以它具有非常小的目的。
但是，这将是图书馆内，我们可以用它来扔掉你打的字符
当你输入到标准英寸
所以hacker_typer.c，我们会想包括的。
的手册页的termios的 - 我猜测它的终端OS或东西 -
我不知道如何读它。
在此，它说，包括这2个文件，所以我们将做到这一点。
>> 第一件事，第一，我们要在一个单一的说法，这是我们要打开的文件。
那么，我该怎么办？我该如何检查，看看我有一个参数呢？
[学生]，如果argc等于它。 >> [鲍登]是啊。
所以，如果（ARGC = 2）输出（“用法：％s [文件打开]”）。
所以，现在如果我没有提供第二个参数 - 哦，我需要新的生产线 -
你会看到它说，使用/ hacker_typer，
第二个参数应该是我要打开的文件。
现在我该怎么办？
我想从这个文件中读取。我如何从文件中读取吗？
[学生]：你先打开它。 >>呀。
所以FOPEN。 FOPEN看是什么样的？
[学生]文件名。 >> [鲍登] filename是要ARGV [1]。
[学生]，然后你想用它做什么，所以 - >> [鲍登]是啊。
所以，如果你不记得，你可能只是做男人FOPEN，
它的将是一个const char * path的路径是文件名，
为const char *模式。
如果你碰巧不记得是什么模式，那么你可以看看模式。
里面的手册页，斜线字符是什么，你可以用它来搜索东西。
所以，我键入搜索模式/模式。
N和N是什么，你可以用它来通过搜索匹配周期。
这里说的参数模式指向一个字符串
开始与下列序列之一。
所以，R，打开文本文件进行读取。这就是我们想要做的。
对于阅读，我要存储。
这件事是怎么回事，是一个FILE *。现在我想要做的是什么呢？
给了我第二次​​。
好吧。现在我想要做的是什么呢？
[学生]检查它是否为NULL。 >> [鲍登]是啊。
任何时候你打开一个文件时，请确保你能够成功打开它。
>> 现在我想做的事，termios的东西，我想先读我的当前设置
并保存到的东西，那么我想改变我的设置
扔掉我键入任何字符，
然后我想更新这些设置。
然后在程序结束时，我想改回我原来的设置。
结构类型termios的，我会想这些。
第一个是是我的current_settings，
然后，他们将是我的hacker_settings。
首先，我要保存我的当前设置，
然后我会要更新hacker_settings，
然后结束时，我的计划，我想恢复到当前设置。
所以保存当前设置的方式工作，我们的人termios的。
我们看到，我们有这样的诠释tcsetattr，诠释tcgetattr。
我通过一个termios结构由它的指针。
这看起来是 - 我已经忘记了调用该函数。
复制和粘贴。
因此，tcgetattr，然后我保存的信息，我想通过在结构中，
这是怎么回事是current_settings，
而第一个参数是文件描述符的事情，我要保存的属性。
任何时候你打开一个文件的文件描述符是，它得到一个文件描述符。
当我FOPEN的argv [1]，它得到了你所引用的文件描述符
每当你要读取或写入到它。
这不是我想在这里使用的文件描述符。
默认情况下，你有三个文件描述符，
这是标准输入，标准输出和标准错误。
默认情况下，我认为它的标准是0，标准输出为1，标准误差为2。
那么，我要更改的设置吗？
我想改变的设置，每当我打了一个字符，
我希望它抛出的字符，而不是打印到屏幕上。
流 - 标准，标准输出和标准错误 -
响应的事情，当我在键盘上键入？ >> [学生]标准英寸>>呀。
所以我可以做0或者我可以做标准输入。
我得到了current_settings标准中。
>> 现在，我想更新这些设置，
所以，我首先复制到hacker_settings我的current_settings的。
结构如何工作，它只会复制。
这会将所有的领域，如你所期望的。
>> 现在，我要更新的一些字段。
在termios的，你就必须读了很多这方面
只是看你要寻找的，
但标志，你会想看看有回音，
所以echo回显输入的字符。
首先，我想设置 - 从来就已经被遗忘的领域是什么。
这是结构看起来像什么。
所以输入模式，我觉得我们要改变。
我们来看看解决方案，以确保这正是我们想要改变。
我们要改变lflag，以防止需要通过所有这些。
我们要改变本地模式。
你将不得不读通过这整个事情了解的一切都属于
我们要改变。
但是它里面的本地模式，我们要去的地方，要改变这种状况。
因此，hacker_settings.cc_lmode是它叫什么。
c_lflag。
这是我们进入按位运算符。
我们种出来的时候，但我们会通过它真正的快速。
这是我们进入按位运算符，
在那里我觉得我说的很久以前，每当你开始处理标志，
你将要使用按位运算符有很多。
标志中的每个位对应于某种行为。
所以在这里，这个标志有一堆不同的东西，所有的人意味着不同的东西。
但我想要做的是关闭位，对应于ECHO。
因此，要打开关闭我做＆=¬ECHO。
其实，我认为这是如TECHO什么。我只是要再次检查。
我可以termios的。这只是回声。
ECHO将是一个单比特。
，ECHO将意味着所有位都设置为1，这意味着所有的标志都设置为true
除了的ECHO位。
结束我的本地标志，这意味着目前所有标志设置为true，
将仍然被设置为true。
如果我的ECHO标志设置为true，那么这是必然的ECHO标志设置为false。
所以，这行代码只是关闭ECHO标志。
行代码，我就复制他们的利益的时间，然后加以解释。
在该解决方案中，他说0。
明确地说，标准输入，它可能会更好。
>> 请注意，我也做ECHO ICANON。
ICANON是指一些独立的，这意味着典型的模式。
典型模式是指的是当你键入的命令行，
标准不处理任何事情，直到你击中换行。
所以当你的GetString，你键入了一堆东西，然后你打换行。
这时候，它的发送到标准中。
这是默认的。
当我关闭典型模式，你按现在每一个字符
是被处理，这通常是有种不好的，因为它是缓慢的处理这些事情，
这就是为什么它是很好的缓冲到整行。
但我想每个字符处理
因为我不希望它等待我打换行
前处理所有的字符，我一直打字。
关闭标准模式。
这东西只是意味着当实际处理字符。
这意味着，他们立即处理;尽快，因为我打字，处理它们。
这是更新的功能，我的设置标准，
和TCSA手段做是正确的。
其他选项等待，，直到流上的一切，这是目前处理。
这其实并不重要。
现在改变我的设置，无论是目前在hacker_typer_settings的。
我想我把它叫做hacker_settings，让我们改变它。
改变一切hacker_settings。
>> 现在，在我们的计划，我们将要恢复
什么是目前的normal_settings内，
这是怎么回事，只是看起来像及normal_settings。
请注意，我并​​没有改变任何，我normal_settings，因为最初得到它。
然后就改回来，我通过他们在最后。
这是更新。好吧。
>> 内，在这里我就解释中的代码的利益。
这是没有那么多的代码。
我们看到，我们从文件中读取一个字符。我们称它f。
现在，您可以男人fgetc函数，fgetc去上班
只是它会返回你刚才读的字符或EOF，
其对应的端部的文件或一些错误发生。
我们循环，继续从文件中读取一个字符，
直到我们已经用完了要读取的字符。
而我们这样做，我们等待的单个字符标准英寸
每一次你在命令行中键入一些东西，
阅读一个字符从标准中。
随后的putchar只是打算把在这里，我们读到了从文件到标准输出的字符。
男人的putchar，但它只是把输出到标准输出，它的打印字符。
你也可以做输出（“％C”，C）;同样的想法。
这是怎么回事，做大量的工作。
>> 我们想要做的最后一件事是只是FCLOSE我们的文件。
如果你不FCLOSE，这是一个内存泄漏。
我们要FCLOSE我们最初打开的文件，我认为这是它的。
如果我们达到这个目标，我已经有问题。
让我们来看看。
那有什么抱怨吗？
预计'诠释'，但实参的类型结构_IO_FILE *“。
我们可以看到，如果这样的作品。
只允许在C99。 Augh。好吧，让hacker_typer。
现在，我们得到更多有用的描述。
因此，使用未声明的标识符“normal_settings”。
我没有把它normal_settings。我称为它current_settings。
因此，让我们改变这一切。
现在传递参数。
现在，我会让0。
好吧。 / hacker_typer cp.c.
我也并没有明确的开始屏幕上。
但是你可以回头看的最后一个问题集，就看你如何清除屏幕。
这就是打印一些字符
虽然这是做我想做的事情。
好吧。
思考为什么这个需要，而不是标准输入为0，
应＃定义0，
这是抱怨 -
以前我说的文件描述符，但你也有你的FILE *，
一个文件描述符只是一个单一的整数，
而一个FILE *有一大堆的东西，与它相关联的。
究其原因，我们需要说0，而不是标准输入
的是，stdin是一个FILE *，它指向的东西被引用文件描述符0。
因此，即使在这里，当我做FOPEN（ARGV [1]，我得到一个FILE *。
但是，在该文件中*是对应于该文件的文件描述符的事情。
如果你看看在开放的手册页，所以我认为你必须做的人3个开放式的 - 都能跟得上 -
男子2个开放 - 是啊。
如果你看一下在页面的开放，开放是像一个较低的水平FOPEN，
它返回的实际文件描述符。
的FOPEN做了一堆东西之上的开放，
而不是只是返回的文件描述符FILE *指针返回一个整
里面，这是我们的小文件描述符。
所以标准是指FILE *的事情，
而0指的是刚才的文件描述标准本身。
>> 有问题吗？
[笑]吹过。
好的。我们就大功告成了。 [笑]
>> [CS50.TV]