[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是要输出/你好, 重定向标准错误。 因此,如果发生了标准错误,它会不会被投入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]