1 00:00:00,000 --> 00:00:02,500 [Powered by Google Translate] [第5条 - 更舒适] 2 00:00:02,500 --> 00:00:04,690 [罗布·鲍登 - 哈佛大学] 3 00:00:04,690 --> 00:00:07,250 [这是CS50。 - CS50.TV] 4 00:00:08,990 --> 00:00:14,250 >> 就像我在我的电子邮件中说,有很多的东西,你可以使用 5 00:00:14,250 --> 00:00:17,060 其他比设备实际做的习题集。 6 00:00:17,060 --> 00:00:19,910 我们建议你这样做只是因为它在家电,那么我们就可以更容易地帮助您 7 00:00:19,910 --> 00:00:22,070 我们知道如何去上班。 8 00:00:22,070 --> 00:00:26,950 但是,作为一个例子,你可以做的事情,如果说,你不能够访问 9 00:00:26,950 --> 00:00:31,570 的设备或您要在科学中心地下室 - 10 00:00:31,570 --> 00:00:33,090 其实他们有设备 - 11 00:00:33,090 --> 00:00:35,150 如果你想在任何地方工作。 12 00:00:35,150 --> 00:00:42,370 一个例子是,你看到/听到的SSH? 13 00:00:44,380 --> 00:00:47,780 SSH是基本上是一样的东西连接。 14 00:00:47,780 --> 00:00:51,340 其实,现在我SSHed插入设备。 15 00:00:51,340 --> 00:00:54,290 我从来没有直接在产品中。 16 00:00:55,930 --> 00:01:01,060 这里是家电,如​​果你看这里,你看到这个IP地址。 17 00:01:01,060 --> 00:01:03,650 我从来没有在设备本身; 18 00:01:03,650 --> 00:01:08,840 我总是iTerm2窗口/终端窗口。 19 00:01:08,840 --> 00:01:15,910 可以SSH到的IP地址,SSH jharvard@192.168.129.128。 20 00:01:15,910 --> 00:01:20,390 我记得这个数字很容易,因为它是一个很好的模式。 21 00:01:20,390 --> 00:01:24,920 但是,这将问我,我的密码,现在我在家电。 22 00:01:24,920 --> 00:01:33,060 基本上,在这一点上,如果打开了一个终端设备本身的内部, 23 00:01:33,060 --> 00:01:36,350 这个接口,但是你会使用它,是完全一样的 24 00:01:36,350 --> 00:01:40,010 使用的接口,我在这里,但现在你SSHed。 25 00:01:42,240 --> 00:01:44,920 你没有SSH的设备。 26 00:01:44,920 --> 00:01:52,360 我敢肯定,你必须在默认情况下,另一个地方,你可以SSH是一个例子 - 27 00:01:52,360 --> 00:01:55,020 哦。更大。 28 00:01:55,020 --> 00:02:01,130 所有的人都应该有默认情况下,FAS FAS服务器的帐户。 29 00:02:01,130 --> 00:02:06,840 对于我来说,我会SSH的rbowden@nice.fas.harvard.edu。 30 00:02:06,840 --> 00:02:11,610 它会问你的第一次,和你说的话。 31 00:02:11,610 --> 00:02:15,840 我的密码,只是将我的FAS密码。 32 00:02:15,840 --> 00:02:22,650 所以现在,我SSHed漂亮的服务器,和我想在这里,我可以做任何事情。 33 00:02:22,650 --> 00:02:28,560 很多类,你可能需要像124,有你上传的东西在这里 34 00:02:28,560 --> 00:02:30,950 提交您的问题集。 35 00:02:30,950 --> 00:02:34,100 但说你没有访问您的设备。 36 00:02:34,100 --> 00:02:37,910 然后你就可以像在这里做的事情,它会说 - 37 00:02:37,910 --> 00:02:42,160 这仅仅是我们部分的问题。 38 00:02:42,160 --> 00:02:45,070 它会问你做到这一点的设备。 39 00:02:45,070 --> 00:02:47,790 相反,我会做它的服务器上。 40 00:02:47,790 --> 00:02:50,560 我要解压缩。 41 00:02:50,560 --> 00:02:55,670 这个问题将是你已经习惯了使用的东西,例如gedit 42 00:02:55,670 --> 00:02:58,160 或其他组件内。 43 00:02:58,160 --> 00:03:01,830 你不会的FAS服务器上。 44 00:03:01,830 --> 00:03:04,110 这一切都只是将这个文本界面。 45 00:03:04,110 --> 00:03:09,180 所以,你可以任何一个,努力学习,他们有一个文本编辑器。 46 00:03:09,180 --> 00:03:12,130 他们有纳米。 47 00:03:12,130 --> 00:03:14,990 纳米通常是很容易使用。 48 00:03:14,990 --> 00:03:19,470 您可以使用箭头,然后键入正常。 49 00:03:19,470 --> 00:03:21,250 因此,并不难。 50 00:03:21,250 --> 00:03:24,720 如果你想获得真正看中的,你可以使用Emacs, 51 00:03:24,720 --> 00:03:29,850 我也许不应该打开了,因为我什至不知道如何关闭Emacs的。 52 00:03:29,850 --> 00:03:32,760 控制X,控制C?是啊。 53 00:03:32,760 --> 00:03:35,310 或者你也可以使用vim,这是我使用。 54 00:03:35,310 --> 00:03:37,800 所以这些都是你的选择。 55 00:03:37,800 --> 00:03:43,830 如果你不想这样做,你也可以,如果你manual.cs50.net - 56 00:03:43,830 --> 00:03:45,410 哦。 57 00:03:45,410 --> 00:03:49,920 在PC上,你可以使用SSH使用PuTTY 58 00:03:49,920 --> 00:03:51,940 你要单独下载。 59 00:03:51,940 --> 00:03:55,460 在Mac上,你可以在默认情况下使用的终端或你可以下载iTerm2, 60 00:03:55,460 --> 00:03:58,490 这是一个很好的,看中的终端。 61 00:03:58,490 --> 00:04:03,780 如果你去到manual.cs50.net,你会看到一个链接到记事本+ +, 62 00:04:03,780 --> 00:04:07,120 这是你可以在PC上使用。 63 00:04:07,120 --> 00:04:13,340 它可以让你SFTP从记事本+ +,这基本上是SSH。 64 00:04:13,340 --> 00:04:17,750 这是什么让你这样做是在本地编辑您的文件, 65 00:04:17,750 --> 00:04:20,670 然后,每当你想将它们保存,它会保存到nice.fas中, 66 00:04:20,670 --> 00:04:23,670 然后,你可以运行它们。 67 00:04:23,670 --> 00:04:26,880 在Mac上相当于将是TextWrangler。 68 00:04:26,880 --> 00:04:28,760 因此,它可以让你做同样的事情。 69 00:04:28,760 --> 00:04:32,800 它可以让你在本地编辑文件,将它们保存到nice.fas, 70 00:04:32,800 --> 00:04:35,730 然后,你可以运行它们。 71 00:04:35,730 --> 00:04:40,400 所以,如果你遇到难题没有设备,你有这些选项 72 00:04:40,400 --> 00:04:44,230 还是做你的问题集。 73 00:04:44,230 --> 00:04:48,250 一个问题是,你不会有CS50库 74 00:04:48,250 --> 00:04:51,580 因为nice.fas默认情况下,不具备这一点。 75 00:04:51,580 --> 00:04:55,970 您可以下载的CS50库 - 76 00:04:55,970 --> 00:04:58,470 我不认为我需要在这一点上。 77 00:04:58,470 --> 00:05:03,270 你可以下载的的CS50库和复制到nice.fas, 78 00:05:03,270 --> 00:05:07,450 我认为在这一点上,我们不使用它了呢。 79 00:05:07,450 --> 00:05:12,720 或者,如果我们这样做,你的时间可以被取代它 80 00:05:12,720 --> 00:05:18,480 反正的CS50库中的功能的实现。 81 00:05:18,480 --> 00:05:21,370 因此,不应该有那么多的限制。 82 00:05:21,370 --> 00:05:23,710 就是这样。 83 00:05:26,460 --> 00:05:29,820 >> 我会回去的设备,现在我们将尽一切设备。 84 00:05:29,820 --> 00:05:37,510 我们的部分问题,在开始的时候,就像我在我的电子邮件中说, 85 00:05:37,510 --> 00:05:43,620 我们要谈一个短你应该看。 86 00:05:43,620 --> 00:05:51,980 我们有重定向和管道这三个问题。 87 00:05:51,980 --> 00:05:56,070 >> 在这流功能,如输出默认情况下写的? 88 00:05:56,070 --> 00:05:59,130 所以流。什么是流? 89 00:06:06,520 --> 00:06:15,100 甲流是基本上一样,只是一些 - 90 00:06:15,100 --> 00:06:21,450 它甚至不是一个源1s和0s。 91 00:06:21,450 --> 00:06:24,920 这里要求的是标准输出流。 92 00:06:24,920 --> 00:06:27,250 因此,标准输出流,当你写它, 93 00:06:27,250 --> 00:06:30,940 它出现在屏幕上。 94 00:06:30,940 --> 00:06:36,860 标准输出流,这意味着你只写1“和”0“到它, 95 00:06:36,860 --> 00:06:40,220 标准输出的另一端从该流中读取数据。 96 00:06:40,220 --> 00:06:43,540 这只是一个字符串1s和0s。 97 00:06:43,540 --> 00:06:45,570 你可以写流,或者你可以从数据流中读取 98 00:06:45,570 --> 00:06:47,950 根据流实际上是什么。 99 00:06:47,950 --> 00:06:52,800 其他两个默认流的标准和标准错误。 100 00:06:52,800 --> 00:06:57,540 标准的是每当你的GetString,它正在等待你输入的东西。 101 00:06:57,540 --> 00:07:01,570 因此,它在等着你,它实际上是在等待标准, 102 00:07:01,570 --> 00:07:04,880 这是真的,你会得到什么,当你在键盘上输入。 103 00:07:04,880 --> 00:07:07,530 你输入标准英寸 104 00:07:07,530 --> 00:07:10,050 标准错误是基本上等同于标准输出, 105 00:07:10,050 --> 00:07:13,280 但它的专业,当您打印到标准错误, 106 00:07:13,280 --> 00:07:16,770 你应该只打印错误消息,该 107 00:07:16,770 --> 00:07:20,200 这样你就可以区分常规消息打印到屏幕上 108 00:07:20,200 --> 00:07:24,560 对误差取决于他们是否去到标准输出和标准错误的消息。 109 00:07:24,560 --> 00:07:28,660 文件了。 110 00:07:28,660 --> 00:07:32,440 标准输出,标准和标准错误是特别的流, 111 00:07:32,440 --> 00:07:36,810 但实际上任何文件,当你打开一个文件时,它变成一个字节流 112 00:07:36,810 --> 00:07:40,740 在那里你可以从该流中读取。 113 00:07:40,740 --> 00:07:47,770 您,在大多数情况下,只是觉得一个文件的字节流。 114 00:07:47,770 --> 00:07:51,190 所以他们写什么流在默认情况下?标准输出。 115 00:07:51,190 --> 00:07:56,980 >> >和>>之间的区别是什么? 116 00:07:58,140 --> 00:08:03,710 没有人事先观看视频?好吧。 117 00:08:03,710 --> 00:08:10,960 >将是如何重定向到文件, 118 00:08:10,960 --> 00:08:15,240 >>也将输出重定向到文件中, 119 00:08:15,240 --> 00:08:17,820 但它的,而不是要追加到文件。 120 00:08:17,820 --> 00:08:23,430 例如,让我们说,我正好有字典在这里, 121 00:08:23,430 --> 00:08:27,020 唯一的字典里面的东西是猫,猫,狗,鱼,狗。 122 00:08:27,020 --> 00:08:31,530 一个命令,你必须在命令行是猫, 123 00:08:31,530 --> 00:08:34,539 只是要打印在一个文件中。 124 00:08:34,539 --> 00:08:40,679 所以当我说猫字典,它要打印的猫,猫,狗,鱼,狗。这是所有的猫做。 125 00:08:40,679 --> 00:08:46,280 这意味着,猫,猫,狗,鱼,狗,它打印到标准输出。 126 00:08:46,280 --> 00:08:53,240 如果我不是要重定向到一个文件中,我可以使用>将它重定向到任何该文件是。 127 00:08:53,240 --> 00:08:56,460 我会打电话给该文件的文件。 128 00:08:56,460 --> 00:09:00,320 所以,现在如果我LS,我会看到我有一个新的文件名为文件。 129 00:09:00,320 --> 00:09:05,700 如果我打开它,它将会有什么猫放在命令行。 130 00:09:05,700 --> 00:09:11,040 所以,现在如果我这样做了,那么它的输出重定向到文件, 131 00:09:11,040 --> 00:09:13,930 和我有同样的事情。 132 00:09:13,930 --> 00:09:17,910 所以,从技术上来说,它完全推翻了我们什么。 133 00:09:17,910 --> 00:09:22,970 我们会看到,如果我改变了字典,我拿出狗。 134 00:09:22,970 --> 00:09:29,980 现在,如果我们猫词典到文件中,我们将有新的版本与狗删除。 135 00:09:29,980 --> 00:09:32,400 因此,它完全覆盖。 136 00:09:32,400 --> 00:09:36,640 相反,如果我们使用“>>”,这是怎么回事追加文件。 137 00:09:36,640 --> 00:09:40,860 现在,打开文件,我们可以看到,我们有同样的事情两次印刷 138 00:09:40,860 --> 00:09:44,920 有一次,因为它是,那么我们就追加到原来的。 139 00:09:44,920 --> 00:09:48,130 所以,这就是>和>>。 140 00:09:48,130 --> 00:09:50,580 请问下一个要求 - 它不要求它。 141 00:09:50,580 --> 00:09:59,050 >> 我们有另一种是<,如果标准输出重定向, 142 00:09:59,050 --> 00:10:01,970 <将被重定向标准英寸 143 00:10:01,970 --> 00:10:12,050 让我们来看看,如果我们有一个例子。 144 00:10:14,750 --> 00:10:16,930 我可以写一个真正的快速。 145 00:10:17,870 --> 00:10:25,700 让我们任何文件hello.c。 146 00:10:56,060 --> 00:10:59,070 相对直截了当文件。 147 00:10:59,070 --> 00:11:03,570 我只是一个字符串,然后打印“Hello”无论我刚刚输入的字符串是。 148 00:11:03,570 --> 00:11:07,990 因此,请hello,然后。/个招呼。 149 00:11:07,990 --> 00:11:10,720 现在,它提示我输入的东西, 150 00:11:10,720 --> 00:11:15,070 这意味着它等待的东西,将予订立标准英寸 151 00:11:15,070 --> 00:11:20,450 因此,进入任何我想要的标准。我们要好好说“你好”,罗布! 152 00:11:20,450 --> 00:11:23,310 然后打印到标准输出您好,来抢! 153 00:11:23,310 --> 00:11:28,860 如果我这样做。/ hello,然后重定向, 154 00:11:30,740 --> 00:11:34,310 现在你可以只从一个文件重定向。 155 00:11:34,310 --> 00:11:41,720 所以,如果我把一些文件,TXT,我把罗布, 156 00:11:41,720 --> 00:11:52,300 如果我跑个招呼,然后重定向到的文件文本。/打招呼,它会说“你好”,抢!立即。 157 00:11:52,300 --> 00:11:57,160 当它第一次得到的GetString和它的等待标准, 158 00:11:57,160 --> 00:12:01,730 标准中不再等待键盘上的数据,从而得到进入。 159 00:12:01,730 --> 00:12:05,980 相反,我们从文件中读取TXT重定向标准。 160 00:12:05,980 --> 00:12:10,290 因此,它会读取TXT的文件,这仅仅是行抢, 161 00:12:10,290 --> 00:12:13,380 那么它会打印Hello,罗布! 162 00:12:13,380 --> 00:12:18,180 如果我想,我也可以做。/你好TXT 163 00:12:18,180 --> 00:12:21,500 然后指出它的标准印刷,您好,这是来抢! 164 00:12:21,500 --> 00:12:24,700 我可以重定向到它自己的文件。 165 00:12:24,700 --> 00:12:29,790 我只需要调用的文件招呼 - 不,我不会,因为这是可执行文件 - txt2。 166 00:12:29,790 --> 00:12:40,150 现在,,txt2是要输出/你好 00:12:43,520 >> 有问题吗? 168 00:12:45,900 --> 00:12:49,090 >> 好吧。那么在这里,我们有管道。 169 00:12:49,090 --> 00:12:53,510 管道的最后一个单元的重定向。 170 00:12:53,510 --> 00:12:58,750 >> 哦。我想一个单位的重定向,如果不是你2>, 171 00:12:58,750 --> 00:13:01,070 重定向标准错误。 172 00:13:01,070 --> 00:13:06,280 因此,如果发生了标准错误,它会不会被投入txt2。 173 00:13:06,280 --> 00:13:12,480 但是请注意,如果我这样做2>,然后它仍然印刷您好,来抢!在命令行 174 00:13:12,480 --> 00:13:18,600 因为我只重定向标准错误,我不重定向标准输出。 175 00:13:18,600 --> 00:13:22,210 标准错误和标准输出是不同的。 176 00:13:24,210 --> 00:13:27,080 如果你想真正写入到标准错误, 177 00:13:27,080 --> 00:13:35,080 然后我可以改变这是fprintf到stderr。 178 00:13:35,080 --> 00:13:37,850 所以,默认情况下,打印输出到标准输出。 179 00:13:37,850 --> 00:13:41,720 如果我想手动打印到标准错误,那么我必须使用fprintf 180 00:13:41,720 --> 00:13:45,010 并指定我要打印到的。 181 00:13:45,010 --> 00:13:49,720 相反,如果我做了fprintf标准输出,那么这基本上等同于printf的。 182 00:13:49,720 --> 00:13:55,530 ,但fprintf标准错误。 183 00:13:57,790 --> 00:14:03,650 所以,现在,如果我重定向到txt2,您好,来抢!还是会被印在命令行 184 00:14:03,650 --> 00:14:08,270 ,因为它的打印到标准错误,我只重定向标准输出。 185 00:14:08,270 --> 00:14:16,420 如果我现在将标准错误重定向,现在也没有打印出来,和txt2将是你好,抢! 186 00:14:16,420 --> 00:14:21,910 那么现在,你可以打印你的实际标准错误的错误 187 00:14:21,910 --> 00:14:24,720 打印您的的常规邮件到标准输出。 188 00:14:24,720 --> 00:14:31,420 所以当你运行你的程序,你可以运行它。/您好,这类型的2> 189 00:14:31,420 --> 00:14:33,800 让你的程序将正常运行, 190 00:14:33,800 --> 00:14:38,400 但任何错误消息,让你可以检查你的错误日志中, 191 00:14:38,400 --> 00:14:44,500 这样的错误,然后看后,你的错误,文件中有任何错误发生。 192 00:14:45,200 --> 00:14:47,540 >> 有问题吗? 193 00:14:47,540 --> 00:14:58,070 >> 最后一个是管道,你能想到的,所承担的标准,从一个命令 194 00:14:58,070 --> 00:15:01,210 和它的标准中的下一个命令。 195 00:15:01,210 --> 00:15:05,570 这里是一个例子,echo是一个命令行的东西 196 00:15:05,570 --> 00:15:11,840 这只是呼应,我把什么作为它的参数。我不会把引号。 197 00:15:11,840 --> 00:15:16,150 回音胡说,胡说,胡说,只是要打印等等,等等,等等。 198 00:15:16,150 --> 00:15:20,600 在此之前,当我说,我只好把罗布到一个txt文件 199 00:15:20,600 --> 00:15:28,830 因为我只能重定向txt文件,而不是,/如果我呼应罗布 200 00:15:28,830 --> 00:15:35,520 然后通过管道到/个招呼,这也将做同样类型的东西。 201 00:15:35,520 --> 00:15:39,160 这是此命令的输出,回声罗布, 202 00:15:39,160 --> 00:15:43,610 并用它作为输入/个招呼。 203 00:15:44,790 --> 00:15:49,560 你可以认为它是第一个到一个文件重定向回声罗布 204 00:15:49,560 --> 00:15:54,160 ,然后输入/你好,文件只是输出。 205 00:15:54,160 --> 00:15:57,850 但它需要的临时文件的图片。 206 00:16:01,890 --> 00:16:04,460 >> 该问题吗? 207 00:16:04,460 --> 00:16:07,150 >> 接下来的问题是会涉及到这一点。 208 00:16:07,150 --> 00:16:15,310 管道,你可以用它来寻找一个称为names.txt的文件的唯一名称是什么? 209 00:16:15,310 --> 00:16:24,160 ,我们将要在这里使用的命令是唯一的,因此uniq中,然后厕所。 210 00:16:24,160 --> 00:16:28,840 你可以做男人的uniq的实际看是什么做的, 211 00:16:28,840 --> 00:16:34,840 它只是将筛选相邻的匹配行从输入。 212 00:16:34,840 --> 00:16:40,690 男子厕所是要打印的换行,字和字节计数的每个文件。 213 00:16:40,690 --> 00:16:43,760 我们将要使用的是最后一个排序, 214 00:16:43,760 --> 00:16:47,410 这是怎么回事,只是txt文件的行进行排序。 215 00:16:47,410 --> 00:16:58,080 如果我做一些txt文件,的names.txt,它的罗布,张宇,约瑟夫,张宇,约瑟夫,RJ,罗布, 216 00:16:58,080 --> 00:17:03,910 我想在这里做什么的,是在这个文件中找到的唯一名称。 217 00:17:03,910 --> 00:17:08,750 那么应该怎样的答案呢? >> [学生] 4。 >>呀。 218 00:17:08,750 --> 00:17:13,780 它应该是4,因为罗布,汤米,约瑟夫,RJ是在这个文件中只有唯一的名称。 219 00:17:13,780 --> 00:17:20,180 第一步,如果我只是做的字数的names.txt, 220 00:17:20,180 --> 00:17:24,290 这实际上是告诉了我一切。 221 00:17:24,290 --> 00:17:32,560 其实,这是印刷 - 让我们来看看,男人WC - 换行,单词和字节计数。 222 00:17:32,560 --> 00:17:38,270 如果我只在乎线,然后我就可以做WC-L names.txt。 223 00:17:41,730 --> 00:17:44,300 所以这是第1步。 224 00:17:44,300 --> 00:17:50,510 但我不希望到厕所 - 1 names.txt,因为names.txt只包含了所有的名字, 225 00:17:50,510 --> 00:17:54,170 我要过滤掉任何非唯一的。 226 00:17:54,170 --> 00:18:01,200 所以,如果我做的uniq names.txt,不很给我我想要的 227 00:18:01,200 --> 00:18:03,760 因为重复的名称仍然存在。 228 00:18:03,760 --> 00:18:07,690 这是为什么? uniq的是为什么不这样做我想要什么? 229 00:18:07,690 --> 00:18:10,500 [学生]重复[听不清] >>呀。 230 00:18:10,500 --> 00:18:16,370 记住的uniq手册页说,过滤器相邻的匹配行。 231 00:18:16,370 --> 00:18:19,680 他们是不相邻的,所以它不会过滤。 232 00:18:19,680 --> 00:18:31,100 如果我先对它们进行排序,排序names.txt是打算把所有重复的行。 233 00:18:31,100 --> 00:18:34,450 所以,现在排序names.txt是。 234 00:18:34,450 --> 00:18:40,550 我将要使用的uniq的,这是| uniq的输入。 235 00:18:40,550 --> 00:18:43,390 这给了我约瑟夫,RJ,抢夺,汤米, 236 00:18:43,390 --> 00:18:49,260 我想使用这个输入到wc-L, 237 00:18:49,260 --> 00:18:52,740 这是要给我4。 238 00:18:52,740 --> 00:18:56,930 喜欢它说,在这里,你可以使用什么样的管道? 239 00:18:56,930 --> 00:19:01,390 你可以做很多的事情,比如使用一系列的命令 240 00:19:01,390 --> 00:19:05,130 在您使用一个命令的输出作为下一个命令的输入。 241 00:19:05,130 --> 00:19:08,780 你可以做很多事情,很多聪明的事情。 242 00:19:08,780 --> 00:19:11,440 >> 有问题吗? 243 00:19:12,910 --> 00:19:14,600 好吧。 244 00:19:14,600 --> 00:19:17,880 这是它的管道和重定向。 245 00:19:18,370 --> 00:19:24,090 >> 现在我们的实际的东西,编码的东西。 246 00:19:24,090 --> 00:19:29,100 本PDF里面,你会看到这个命令, 247 00:19:29,100 --> 00:19:32,950 你要运行此命令在您的设备。 248 00:19:36,240 --> 00:19:42,250 wget是命令只是从互联网上得到的东西,基本上, 249 00:19:42,250 --> 00:19:45,180 所以wget和URL。 250 00:19:45,180 --> 00:19:49,110 如果你到这个URL在浏览器中,它会下载该文件。 251 00:19:49,110 --> 00:19:52,510 我只是点击就可以了,所以对我来说,它下载的文件。 252 00:19:52,510 --> 00:19:55,650 但是,写的那个东西的wget内的终端 253 00:19:55,650 --> 00:19:58,620 只是要下载到您的终端。 254 00:19:58,620 --> 00:20:02,750 我有section5.zip的,和你要解压section5.zip的, 255 00:20:02,750 --> 00:20:06,520 这是给你一个文件夹,名为第5章, 256 00:20:06,520 --> 00:20:11,550 这是我们要使用它里面的所有的文件。 257 00:20:33,380 --> 00:20:37,710 随着这些项目的文件名称所暗示的,他们是一个有点缺陷, 258 00:20:37,710 --> 00:20:40,990 所以,你的任务是要弄清楚为什么使用gdb。 259 00:20:40,990 --> 00:20:44,560 每个人都有他们下载/知道如何让他们下载 260 00:20:44,560 --> 00:20:47,480 设备到他们的吗?好吧。 261 00:20:47,480 --> 00:20:56,400 >> 运行./buggy1,它会说分割故障(核心转储) 262 00:20:56,400 --> 00:21:00,500 任何时候你得到一个segfault,这是一个不好的事情。 263 00:21:00,500 --> 00:21:03,810 在什么情况下,你得到一个segfault? 264 00:21:03,810 --> 00:21:08,210 [学生]:取消引用一个空指针。 >>呀。所以这是一个例子。 265 00:21:08,210 --> 00:21:11,580 解引用一个空指针,你会得到一个segfault。 266 00:21:11,580 --> 00:21:16,720 一个segfault手段是你感动的回忆,你不应该接触。 267 00:21:16,720 --> 00:21:21,350 因此,释放空指针动人的地址为0, 268 00:21:21,350 --> 00:21:28,060 基本上,现在所有的电脑说,地址为0的内存,你不应该接触。 269 00:21:28,060 --> 00:21:31,920 所以这就是为什么一个空指针解引用一个segfault。 270 00:21:31,920 --> 00:21:37,210 当你的事发生在初始化的指针,那么它有一个垃圾值, 271 00:21:37,210 --> 00:21:41,520 因此,当您尝试取消引用它,在所有的可能性你感动的回忆 272 00:21:41,520 --> 00:21:43,540 这是在中间的地方。 273 00:21:43,540 --> 00:21:45,650 如果你碰巧很幸运,垃圾的价值 274 00:21:45,650 --> 00:21:48,440 发生点在堆栈上或东西的地方, 275 00:21:48,440 --> 00:21:50,820 那么当你解引用指针,你还没有初始化, 276 00:21:50,820 --> 00:21:52,730 什么都不会出问题。 277 00:21:52,730 --> 00:21:55,480 但是,如果它指向的栈和堆之间的某个地方,说, 278 00:21:55,480 --> 00:21:59,850 它指向只是到别的地方,没有被使用你的程序, 279 00:21:59,850 --> 00:22:02,240 那么你感动的回忆,你不应该接触和你的段错误。 280 00:22:02,240 --> 00:22:06,370 当你写一个递归函数,递归太多次 281 00:22:06,370 --> 00:22:08,720 堆栈的增长过大,堆栈碰撞到的东西 282 00:22:08,720 --> 00:22:12,270 它不应该被碰撞,你感动的回忆,你不应该接触, 283 00:22:12,270 --> 00:22:14,810 因此你段错误。 284 00:22:14,810 --> 00:22:17,010 这是一个segfault。 285 00:22:17,010 --> 00:22:21,810 >> 这是同样的道理,如果你有这样的字符串 - 286 00:22:21,810 --> 00:22:23,930 让我们回到前面的程序。 287 00:22:23,930 --> 00:22:28,530 在hello.c的,我只是要别的东西。 288 00:22:28,530 --> 00:22:33,770 的char * s =“世界你好!”; 289 00:22:33,770 --> 00:22:42,310 如果我使用的东西或S * S = [0] ='X'; 290 00:22:42,310 --> 00:22:47,290 所以一定要打招呼,/你好,为什么是段错误? 291 00:22:48,410 --> 00:22:51,250 为什么这个段错误呢? 292 00:22:55,660 --> 00:22:57,890 你会想到什么事情发生呢? 293 00:22:57,890 --> 00:23:06,640 如果我做了printf的(“%s \ n”);你会想到要打印的吗? 294 00:23:06,640 --> 00:23:09,930 [学生] X打招呼。 >>呀。 295 00:23:09,930 --> 00:23:15,140 现在的问题是,当你声明一个这样的字符串, 296 00:23:15,140 --> 00:23:18,190 s是一个指针,要在栈上, 297 00:23:18,190 --> 00:23:25,880 什么s指向的是这个字符串包含在只读存储器。 298 00:23:25,880 --> 00:23:30,560 因此,只要名称,只读存储器,你应该得到的想法 299 00:23:30,560 --> 00:23:33,010 如果你试图改变什么只读存储器, 300 00:23:33,010 --> 00:23:36,670 你正在做的事情你不应该做的内存和段错误。 301 00:23:36,670 --> 00:23:45,360 这实际上是一个很大的区别的char *和char []。 302 00:23:45,360 --> 00:23:48,790 所以个char [],现在这个字符串将被放在堆栈中, 303 00:23:48,790 --> 00:23:53,960 且堆栈无法读取,这意味着,这应该工作完全正常。 304 00:23:55,500 --> 00:23:57,370 它。 305 00:23:57,370 --> 00:24:06,250 请记住,当我这样做的char * s =“世界你好!”,S本身是在栈上 306 00:24:06,250 --> 00:24:10,390 但s指向其他地方,其他地方恰好是只读。 307 00:24:10,390 --> 00:24:15,640 但个char []是在栈上的东西。 308 00:24:17,560 --> 00:24:21,760 所以这是一个segfault发生的另一个例子。 309 00:24:21,760 --> 00:24:27,820 >> ,我们看到./buggy1导致一个segfault。 310 00:24:27,820 --> 00:24:31,810 从理论上讲,你不应该看buggy1.c立即。 311 00:24:31,810 --> 00:24:35,170 相反,我们将看看它通过gdb的。 312 00:24:35,170 --> 00:24:37,750 请注意,当你得到分割故障(核心转储), 313 00:24:37,750 --> 00:24:40,850 你得到这个文件,这里所说的核心。 314 00:24:40,850 --> 00:24:45,200 如果我们的ls-l,我们可以看到,核心通常是一个相当大的文件。 315 00:24:45,200 --> 00:24:51,580 这是该文件的字节数,所以它看起来像它的250岁的千字节为单位。 316 00:24:51,580 --> 00:24:56,120 这样做的原因是什么实际上是核心转储 317 00:24:56,120 --> 00:25:01,410 是当你的程序崩溃时,你的程序的内存状态 318 00:25:01,410 --> 00:25:05,230 刚刚被复制,并粘贴到该文件中。 319 00:25:05,230 --> 00:25:07,270 它被倾倒到该文件中。 320 00:25:07,270 --> 00:25:13,060 这个程序,运行时,正巧有一个约250千字节的内存使用情况, 321 00:25:13,060 --> 00:25:17,040 所以这是什么得到了倾入这个文件。 322 00:25:17,040 --> 00:25:23,630 现在,你可以看一下该文件,如果我们做GDB buggy1核心。 323 00:25:23,630 --> 00:25:30,130 我们可以做gdb的buggy1,将只启动gdb的定期, 324 00:25:30,130 --> 00:25:33,800 使用buggy1作为它的输入文件。 325 00:25:33,800 --> 00:25:38,260 但是,如果你GDB buggy1的核心,那么它明确要启动GDB 326 00:25:38,260 --> 00:25:40,330 看,核心文件。 327 00:25:40,330 --> 00:25:45,560 你说buggy1手段gdb的都知道,该核心文件来从buggy1程序,。 328 00:25:45,560 --> 00:25:49,580 GDB buggy1核心是要立即给我们带来 329 00:25:49,580 --> 00:25:52,060 的地方发生的程序终止。 330 00:25:57,720 --> 00:26:02,340 我们在这里看到的信号,分割故障终止程序。 331 00:26:02,340 --> 00:26:10,110 我们碰巧看到的组装线,这可能是非常有帮助的。 332 00:26:10,110 --> 00:26:15,360 但是,如果你键入BT或回溯,这将是功能 333 00:26:15,360 --> 00:26:19,430 这样,就为我们提供了我们当前的堆栈帧的列表。 334 00:26:19,430 --> 00:26:23,150 所以回溯。它看起来像我们只有两个堆栈帧。 335 00:26:23,150 --> 00:26:26,310 首先是我们的主要的堆栈帧, 336 00:26:26,310 --> 00:26:29,810 第二个是,我们正好是在这个函数的堆栈帧, 337 00:26:29,810 --> 00:26:34,440 看起来我们只需要的汇编代码。 338 00:26:34,440 --> 00:26:38,050 所以,让我们回到我们的主要功能, 339 00:26:38,050 --> 00:26:42,300 做到这一点,我们可以做的第1帧,我觉得我们也可以做下来, 340 00:26:42,300 --> 00:26:45,160 但我几乎从来没有办下来 - 或上升。是啊。 341 00:26:45,160 --> 00:26:50,710 向上和向下。最多给你带来一个堆栈帧,同比下降给你带来了一个堆栈帧。 342 00:26:50,710 --> 00:26:53,240 我倾向于从来没有使用它。 343 00:26:53,240 --> 00:26:59,120 我只是具体说,这是去的帧标记为1的第1帧。 344 00:26:59,120 --> 00:27:01,750 第1帧,将要带给我们的到主堆栈帧, 345 00:27:01,750 --> 00:27:05,570 在这里,它说发生在我们的代码行。 346 00:27:05,570 --> 00:27:07,950 如果我们想要一对夫妇更行代码列表,我们可以说, 347 00:27:07,950 --> 00:27:11,280 而这会给我们周围所有的代码行。 348 00:27:11,280 --> 00:27:13,360 我们segfaulted该生产线为6: 349 00:27:13,360 --> 00:27:17,360 (STRCMP(“CS50石头”,ARGV [1])== 0)。 350 00:27:17,360 --> 00:27:24,130 如果没有明显的是,你可以得到它直接从这里只是想,为什么segfaulted。 351 00:27:24,130 --> 00:27:28,800 但是,我们可以把它一步,说,“我为什么要ARGV [1]段错误呢?” 352 00:27:28,800 --> 00:27:38,830 让我们打印的argv [1],它看起来像它的0x0,这是空指针。 353 00:27:38,830 --> 00:27:44,750 我们strcmping CS50石块和空,所以会出现段错误。 354 00:27:44,750 --> 00:27:48,280 又为什么是argv [1]为空? 355 00:27:48,640 --> 00:27:51,280 [学生]:因为我们没有给它任何命令行参数。 356 00:27:51,280 --> 00:27:53,390 是啊。我们没有给它任何命令行参数。 357 00:27:53,390 --> 00:27:58,460 所以./buggy1的argv [0]是./buggy1。 358 00:27:58,460 --> 00:28:02,100 它不会有一个argv [1],因此,会出现段错误。 359 00:28:02,100 --> 00:28:07,450 但是,如果不是,我做的只是CS50,它会说你得到D 360 00:28:07,450 --> 00:28:09,950 ,因为这是它应该做的。 361 00:28:09,950 --> 00:28:15,240 在buggy1.c看,它应该打印“你得到一个D” - 362 00:28:15,240 --> 00:28:20,820 如果argv [1],而不是“CS50岩石”,“你得到了D”,否则,你会得到一个A!“ 363 00:28:20,820 --> 00:28:25,660 因此,如果我们要一个一个,我们需要的比较结果为真, 364 00:28:25,660 --> 00:28:28,710 这意味着,它比较为0。 365 00:28:28,710 --> 00:28:31,100 的argv [1]因此,需要将“CS50岩石”。 366 00:28:31,100 --> 00:28:35,660 如果你在命令行上要做到这一点,你需要使用\逃跑的空间。 367 00:28:35,660 --> 00:28:41,690 因此,CS50 \岩石,你会得到一个A! 368 00:28:41,690 --> 00:28:44,060 如果你不这样做的反斜杠,为什么不工作? 369 00:28:44,060 --> 00:28:47,190 [学生]:这是两个不同的参数。 >>呀。 370 00:28:47,190 --> 00:28:52,540 的argv [1]将是CS50,和argv [2]将是岩石。好吧。 371 00:28:52,540 --> 00:28:56,470 >> 现在./buggy2是再次向段错误。 372 00:28:56,470 --> 00:29:01,880 ,其核心文件,而不用打开它,我们就打开了buggy2直接, 373 00:29:01,880 --> 00:29:05,000 所以GDB buggy2。 374 00:29:05,000 --> 00:29:09,590 现在,如果我们运行我们的程序,那么它会说程序收到信号SIGSEGV, 375 00:29:09,590 --> 00:29:15,530 这是段错误的信号,这是它发生在发生。 376 00:29:15,530 --> 00:29:21,250 我们回溯看,我们看到我们在功能oh_no, 377 00:29:21,250 --> 00:29:23,900 这是所谓的由函数极小,这是所谓的由函数binky, 378 00:29:23,900 --> 00:29:26,460 被称为主。 379 00:29:26,460 --> 00:29:31,680 我们也可以看到这些功能的参数。 380 00:29:31,680 --> 00:29:34,680 极小和宾基的参数为1。 381 00:29:34,680 --> 00:29:44,390 如果我们列出的功能oh_no,我们看到,oh_no,只是做的char * s = NULL; 382 00:29:44,390 --> 00:29:47,410 * s =“BOOM”; 383 00:29:47,410 --> 00:29:50,330 为什么会失败呢? 384 00:29:54,330 --> 00:29:58,380 [学生]无法取消引用空指针? >>呀。 385 00:29:58,380 --> 00:30:06,090 这只是说s是NULL,无论如果出现这种情况,是一个char **, 386 00:30:06,090 --> 00:30:12,070 这取决于你如何解释它,它可能是一个指向一个指针,指向一个字符串的指针 387 00:30:12,070 --> 00:30:15,550 或一个字符串数组。 388 00:30:15,550 --> 00:30:21,430 s为null,* S是一个空指针解引用, 389 00:30:21,430 --> 00:30:24,800 所以这是要崩溃。 390 00:30:24,800 --> 00:30:27,540 这是一个最快捷的方法,你可以可能出现段错误。 391 00:30:27,540 --> 00:30:31,300 这只是宣布一个空指针,并立即段错误。 392 00:30:31,300 --> 00:30:34,570 这是什么oh_no。 393 00:30:34,570 --> 00:30:43,400 如果我们去了一个框架,然后,我们将要进入​​的功能,称为oh_no。 394 00:30:43,400 --> 00:30:44,830 我需要做下来。 395 00:30:44,830 --> 00:30:48,610 如果你不输入命令,你只要再次按下Enter键, 396 00:30:48,610 --> 00:30:52,350 它只是重复前面的命令,你跑了。 397 00:30:52,350 --> 00:30:56,610 我们在第1帧。 398 00:30:56,610 --> 00:31:04,650 上市这一框架内,我们在这里看到的是我们的功能。 399 00:31:04,650 --> 00:31:08,520 你可以打列表,或者你可以做的清单20,它会列出。 400 00:31:08,520 --> 00:31:13,640 该函数的极小的说,如果我是1,然后去的oh_no功能, 401 00:31:13,640 --> 00:31:15,960 其他紧身的功能。 402 00:31:15,960 --> 00:31:18,700 我们知道,我是1,因为我们在这里碰巧看到 403 00:31:18,700 --> 00:31:22,560 被称为参数1,极小的。 404 00:31:22,560 --> 00:31:27,560 或者你也可以不打印我,它会说我是1。 405 00:31:27,560 --> 00:31:33,770 目前,我们正在极小,如果我们去了另一个框架,我们知道我们将最终在宾基。 406 00:31:33,770 --> 00:31:36,600 最多。现在,我们在binky。 407 00:31:36,600 --> 00:31:41,340 列出这个功能 - 在上半场结束前把我的清单 - 408 00:31:41,340 --> 00:31:52,670 它开始了,如果我是0,然后,我们将调用它oh_no,否则打电话极小。 409 00:31:52,670 --> 00:31:57,000 我们知道,我是1,因此它被称为极小的。 410 00:31:57,000 --> 00:32:05,030 ,现在我们在主,主要是int i =兰特()%3; 411 00:32:05,030 --> 00:32:08,790 这仅仅是给你一个随机数是0,1或2。 412 00:32:08,790 --> 00:32:12,780 这将调用binky与数字,它会返回0。 413 00:32:12,780 --> 00:32:16,700 见到这种情景, 414 00:32:16,700 --> 00:32:19,880 步行通过的程序不运行它立即,手动 415 00:32:19,880 --> 00:32:25,400 在主,你可以设置一个破发点,这意味着当我们运行程序 416 00:32:25,400 --> 00:32:31,020 你的程序运行,直到它击中了一个破发点。 417 00:32:31,020 --> 00:32:35,450 因此,运行程序,它会运行,然后它会为主打功能,并停止运行。 418 00:32:35,450 --> 00:32:44,700 现在,我们的主要内,步骤或未来将会给我们带来的下一行代码。 419 00:32:44,700 --> 00:32:47,050 你可以做的步骤或未来。 420 00:32:47,050 --> 00:32:51,800 单击Next,现在我已经设置的rand()%3,这样我们就可以打印i的值, 421 00:32:51,800 --> 00:32:55,280 它会说我是1。 422 00:32:55,280 --> 00:32:58,110 现在它无论我们使用下一个或步骤。 423 00:32:58,110 --> 00:33:01,000 我想它在上一个重要的,但我们希望下次使用。 424 00:33:01,000 --> 00:33:06,000 如果我们使用步骤,我们进入的功能,这意味着在实际的东西看 425 00:33:06,000 --> 00:33:07,940 内发生的事情的binky。 426 00:33:07,940 --> 00:33:10,510 如果我们用下,那么就意味着在功能 427 00:33:10,510 --> 00:33:14,070 只是去我们的主函数中的代码的下一行。 428 00:33:14,070 --> 00:33:17,900 在这条线就在这里,我是在它说的rand()%3; 429 00:33:17,900 --> 00:33:21,320 如果我这样做的一步,将进入实施兰特 430 00:33:21,320 --> 00:33:25,110 并期待在那里发生了什么事,我可以通过rand函数的步骤。 431 00:33:25,110 --> 00:33:26,920 但我不关心rand函数。 432 00:33:26,920 --> 00:33:30,190 我只想去到下一行代码的主,所以我用下。 433 00:33:30,190 --> 00:33:35,800 但我现在关心的binky功能,所以我想步入。 434 00:33:35,800 --> 00:33:37,730 现在,我在binky。 435 00:33:37,730 --> 00:33:42,040 的第一行代码是怎么说的(我== 0),我走了一步, 436 00:33:42,040 --> 00:33:44,930 我们可以看到,我们结束了在极小的。 437 00:33:44,930 --> 00:33:51,620 如果我们列出的东西,我们可以看到,它检查是i = 0。 438 00:33:51,620 --> 00:33:55,470 我是不等于0,所以去的其他条件, 439 00:33:55,470 --> 00:33:59,540 要调用极小的(I)。 440 00:33:59,540 --> 00:34:04,030 你可能会感到困惑。 441 00:34:04,030 --> 00:34:07,380 如果你只是直接看这些线,你可能会认为,如果(我== 0) 442 00:34:07,380 --> 00:34:10,800 好,那我走了一步,现在我在极小的(I), 443 00:34:10,800 --> 00:34:14,120 你可能会认为一定意味着i = 0或东西。 444 00:34:14,120 --> 00:34:18,980 它只是意味着它知道它可以直接粘到线极小的(I)。 445 00:34:18,980 --> 00:34:23,300 由于i是不为0时,下一个步骤是不会结束的else。 446 00:34:23,300 --> 00:34:26,239 Else是不是要停在一条线。 447 00:34:26,239 --> 00:34:31,570 它只是要去到下一行,它实际上可以执行,这是极小的(I)。 448 00:34:31,570 --> 00:34:36,090 步入极小的(I),我们可以看到,如果(我== 1)。 449 00:34:36,090 --> 00:34:42,670 我们知道I = 1,所以当我们一步,我们知道我们要在oh_no 450 00:34:42,670 --> 00:34:46,489 因为I = 1调用函数oh_no,你可以进入, 451 00:34:46,489 --> 00:34:52,969 这是要设置的char * s = NULL,并立即“BOOM”。 452 00:34:54,270 --> 00:34:59,690 然后寻找在实施buggy2, 453 00:34:59,690 --> 00:35:04,590 这一点,我只是得到一个随机数 - 0,1或2 - 呼叫宾基, 454 00:35:04,590 --> 00:35:10,610 如果我是0它要求oh_no的,否则它会调用极小,来了这里。 455 00:35:10,610 --> 00:35:18,100 如果i是1,呼叫oh_no,否则打电话紧身,这即将在这里, 456 00:35:18,100 --> 00:35:20,460 如果是2,调用oh_no。 457 00:35:20,460 --> 00:35:24,720 我什至不认为有一种方法 - 458 00:35:24,720 --> 00:35:30,030 有没有人看到这是一个程序,不会出现段错误的一种方式吗? 459 00:35:30,030 --> 00:35:37,530 因为除非我失去了一些东西,如果我是0,你马上就会出现段错误, 460 00:35:37,530 --> 00:35:41,250 否则你去一个函数,如果我是你段错误, 461 00:35:41,250 --> 00:35:44,540 否则,你去给一个函数,如果我是2段错误。 462 00:35:44,540 --> 00:35:46,810 所以无论你做什么,你段错误。 463 00:35:46,810 --> 00:35:52,380 >> 我想修复它,而不是做的char * s = NULL的方式之一, 464 00:35:52,380 --> 00:35:55,610 你可以MALLOC该字符串的空间。 465 00:35:55,610 --> 00:36:04,230 我们可以做的malloc(大小) - 大小是什么? 466 00:36:09,910 --> 00:36:15,190 [学生](字符)* 5? “这似乎不是吗? 467 00:36:15,190 --> 00:36:21,060 我假定这将工作,如果我真的跑了,但它不是我在寻找什么。 468 00:36:24,400 --> 00:36:32,940 看看s的类型。让我们增加对int *,所以int * X。 469 00:36:32,940 --> 00:36:35,600 我会做的malloc(sizeof(int)的的)。 470 00:36:35,600 --> 00:36:40,490 或者,如果我想要一个数组5,我会做(如sizeof(int)* 5); 471 00:36:40,490 --> 00:36:44,210 如果我有一个int **吗? 472 00:36:46,260 --> 00:36:49,140 那我的malloc吗? 473 00:36:49,140 --> 00:36:53,510 [学生]大小的指针。 >>呀。 (如sizeof(int *)); 474 00:36:53,510 --> 00:36:56,960 同样的事情在这里。 475 00:36:56,960 --> 00:37:01,280 我想(如sizeof(char *)的); 476 00:37:06,170 --> 00:37:12,840 这是怎么回事“轰”的指针,该指针指向分配空间。 477 00:37:12,840 --> 00:37:15,330 我并不需要分配空间的“BOOM” 478 00:37:15,330 --> 00:37:17,210 因为这基本上是我在说什么之前 479 00:37:17,210 --> 00:37:20,870 对char * x =“BOOM”。 480 00:37:20,870 --> 00:37:27,950 “BOOM”已经存在。它发生在存在内存中的只读区域。 481 00:37:27,950 --> 00:37:35,200 但它已经存在,这意味着这行代码,如果s是一个char **, 482 00:37:35,200 --> 00:37:43,900 * S是一个char *,你设置这个char *到指向“BOOM”。 483 00:37:43,900 --> 00:37:50,040 如果我想复制到s的“BOOM”,那我就需要为s分配空间。 484 00:37:55,170 --> 00:38:03,900 * S =我会做的malloc(sizeof(字符)* 5)的; 485 00:38:03,900 --> 00:38:06,210 为什么? 486 00:38:06,210 --> 00:38:10,860 为什么不是4?它看起来像“BOOM”是4个字符。 >> [学生] NULL字符。 487 00:38:10,860 --> 00:38:14,580 是啊。需要空字符的字符串。 488 00:38:14,580 --> 00:38:23,590 现在,我可以做类似的strcat - 复制一个字符串的功能是什么? 489 00:38:23,590 --> 00:38:28,520 [学生]京华山一? “STRCPY。 490 00:38:28,520 --> 00:38:32,700 男人的strcpy。 491 00:38:36,120 --> 00:38:39,590 因此,STRCPY或strncpy()函数。 492 00:38:39,590 --> 00:38:43,410 strncpy()函数是一个比较安全的,因为你可以指定到底有多少个字符, 493 00:38:43,410 --> 00:38:46,190 但在这里并不重要,因为我们知道。 494 00:38:46,190 --> 00:38:50,340 因此,strcpy和寻找的参数。 495 00:38:50,340 --> 00:38:53,100 第一个参数是我们的目标。 496 00:38:53,100 --> 00:38:56,770 第二个参数是我们的源代码。 497 00:38:56,770 --> 00:39:10,310 我们要复制到我们的目的地* S的指针“BOOM”。 498 00:39:10,310 --> 00:39:19,820 你为什么要做到这一点,而不是正是我们之前用的strcpy 499 00:39:19,820 --> 00:39:22,800 * S =“BOOM”? 500 00:39:22,800 --> 00:39:28,630 是有原因的,你可能想这样做,但是那是什么原因呢? 501 00:39:28,630 --> 00:39:31,940 [学生]如果你想改变一些东西在“BOOM”。 >>呀。 502 00:39:31,940 --> 00:39:37,950 现在,我可以做一些事情,比如s [0] ='X'; 503 00:39:37,950 --> 00:39:48,190 ,因为点的堆的堆,空间,s是指向 504 00:39:48,190 --> 00:39:52,320 是一个指针,更在堆上的空间,这是存储“BOOM”。 505 00:39:52,320 --> 00:39:55,150 因此,这“轰”的副本被存储在堆中。 506 00:39:55,150 --> 00:39:58,780 “BOOM”在我们的计划在技术上是有两个副本。 507 00:39:58,780 --> 00:40:03,500 这只是这个“BOOM”字符串常量的第一个, 508 00:40:03,500 --> 00:40:09,250 和“嘭”的第二个副本,,STRCPY创建“嘭”的副本。 509 00:40:09,250 --> 00:40:13,100 但是,“嘭”的副本存储在堆中,堆,你是自由的改变。 510 00:40:13,100 --> 00:40:17,250 堆是只读的,因此这意味着,S [0] 511 00:40:17,250 --> 00:40:20,500 是打算让你改变了价值的“BOOM”。 512 00:40:20,500 --> 00:40:23,130 这将让你改变这些字符。 513 00:40:23,130 --> 00:40:26,640 >> 有问题吗? 514 00:40:27,740 --> 00:40:29,290 好吧。 515 00:40:29,290 --> 00:40:35,500 >> 到buggy3,让GDB buggy3。 516 00:40:35,500 --> 00:40:39,840 我们只要运行它,我们可以看到,我们得到一个segfault。 517 00:40:39,840 --> 00:40:46,550 如果我们回溯,有只有两个函数。 518 00:40:46,550 --> 00:40:52,970 如果我们进入到我们的主函数中,我们看到,我们segfaulted在这条线。 519 00:40:52,970 --> 00:41:00,180 因此,只要在这条线,(线= 0; FGETS这东西不等于NULL; 520 00:41:00,180 --> 00:41:03,770 线+ +)。 521 00:41:03,770 --> 00:41:08,010 我们的前一帧被称为_IO_fgets的。 522 00:41:08,010 --> 00:41:10,720 你会看到有很多内置的C函数, 523 00:41:10,720 --> 00:41:15,350 当你得到段错误,会有非常神秘的功能名称 524 00:41:15,350 --> 00:41:18,090 像这个_IO_fgets。 525 00:41:18,090 --> 00:41:21,770 但是,这是怎么回事涉及到这FGETS通话。 526 00:41:21,770 --> 00:41:25,850 内的某处在这里,我们是段错误。 527 00:41:25,850 --> 00:41:30,340 如果我们对fgets的观点看,我们可以打印缓冲区。 528 00:41:30,340 --> 00:41:41,180 让我们打印 - 哦,不。 529 00:41:48,980 --> 00:41:51,900 打印不完全一样,我希望它去上班。 530 00:41:55,460 --> 00:41:58,000 让我们来看看在实际的程序。 531 00:42:02,200 --> 00:42:09,640 缓冲区是一个字符数组。这是一个128个字符的字符数组。 532 00:42:09,640 --> 00:42:14,980 所以,当我说,打印缓冲区,它要打印的这128个字符, 533 00:42:14,980 --> 00:42:18,300 我的猜测是期望是什么。 534 00:42:18,300 --> 00:42:21,390 我一直在寻找的是打印缓冲区的地址, 535 00:42:21,390 --> 00:42:23,680 但是,这并不真正告诉了我很多。 536 00:42:23,680 --> 00:42:30,770 所以,当我碰巧在这里说×缓冲液,它让我看到0xbffff090, 537 00:42:30,770 --> 00:42:38,690 其中,如果你还记得从早期的一些点,Oxbffff往往是一个堆栈上下的区域。 538 00:42:38,690 --> 00:42:46,020 该协议栈往往只是根据0xc000开始的地方。 539 00:42:46,020 --> 00:42:51,890 看到这个地址,我知道该缓冲区在堆栈上正在发生的事情。 540 00:42:51,890 --> 00:43:04,500 重新启动我的程序运行起来,缓冲,我们看到的是这个字符序列 541 00:43:04,500 --> 00:43:06,530 几乎是毫无意义的。 542 00:43:06,530 --> 00:43:12,270 然后打印文件,这是什么文件是什么样子? 543 00:43:15,120 --> 00:43:17,310 [学生] NULL。 >>呀。 544 00:43:17,310 --> 00:43:22,610 文件是一个的类型FILE *,所以它是一个指针, 545 00:43:22,610 --> 00:43:26,610 该指针的值是空的。 546 00:43:26,610 --> 00:43:33,240 所以FGETS会尝试读取该指针以间接的方式, 547 00:43:33,240 --> 00:43:37,320 但为了访问该指针,它有取消对它的引用。 548 00:43:37,320 --> 00:43:40,550 或者,为了访问它应该被人指指点点,解引用它。 549 00:43:40,550 --> 00:43:43,810 因此,它释放空指针,它的segfaults。 550 00:43:46,600 --> 00:43:48,730 我可以重新启动它。 551 00:43:48,730 --> 00:43:52,170 如果我们打破我们的重点和运行, 552 00:43:52,170 --> 00:43:57,320 第一行代码是char *文件名=“nonexistent.txt”; 553 00:43:57,320 --> 00:44:00,870 这应该给一个相当大的提示,这个计划失败的原因。 554 00:44:00,870 --> 00:44:06,080 键入下,我到下一行,我打开这个文件, 555 00:44:06,080 --> 00:44:11,140 然后我立刻进入我们的产品线,其中一次我打了未来,它会出现段错误。 556 00:44:11,140 --> 00:44:16,880 有没有人想抛出一个原因,我们可能会段错误? 557 00:44:16,880 --> 00:44:19,130 [学生]文件不存在。 >>呀。 558 00:44:19,130 --> 00:44:22,250 这应该是一个提示 559 00:44:22,250 --> 00:44:29,570 每当你打开一个文件时,你需要检查文件是否存在。 560 00:44:29,570 --> 00:44:31,510 所以在这里,“nonexistent.txt”; 561 00:44:31,510 --> 00:44:34,700 当我们阅读FOPEN文件名,我们就需要说 562 00:44:34,700 --> 00:44:45,870 (文件== NULL)说printf(“请文件不存在!” 563 00:44:45,870 --> 00:44:56,340 - 更好 - 文件名);返回1; 564 00:44:56,340 --> 00:45:00,300 所以,现在我们要检查是否为NULL 565 00:45:00,300 --> 00:45:03,930 之前,实际上继续,并试图读取该文件。 566 00:45:03,930 --> 00:45:08,800 我们可以重新创建它,只是看到这样的作品。 567 00:45:11,020 --> 00:45:14,970 我打算包括一个新的生产线。 568 00:45:21,090 --> 00:45:25,290 所以,现在nonexistent.txt不存在。 569 00:45:26,890 --> 00:45:30,040 这样的事情,您应经常检查。 570 00:45:30,040 --> 00:45:33,870 您应经常检查,看看FOPEN返回NULL。 571 00:45:33,870 --> 00:45:38,170 您应经常检查,以确定的malloc不返回NULL, 572 00:45:38,170 --> 00:45:41,410 否则,你段错误。 573 00:45:42,200 --> 00:45:45,930 >> 现在buggy4.c。 574 00:45:49,190 --> 00:45:58,440 运行。我猜这是等待输入或可能无限循环。 575 00:45:58,440 --> 00:46:01,870 是的,它是无限循环。 576 00:46:01,870 --> 00:46:05,560 所以buggy4。它看起来像我们的无限循环。 577 00:46:05,560 --> 00:46:12,590 我们可以突破,主要运行我们的程序。 578 00:46:12,590 --> 00:46:20,180 在gdb,只要你使用的缩写是明确的 579 00:46:20,180 --> 00:46:23,420 或者,他们会为您提供特别的缩写, 580 00:46:23,420 --> 00:46:29,020 那么你就可以使用n下次使用,而不必输入下所有的方式。 581 00:46:29,020 --> 00:46:33,730 现在,我已经打了N一次,我可以只按下回车键继续下 582 00:46:33,730 --> 00:46:36,640 而不是打n输入n输入,n输入。 583 00:46:36,640 --> 00:46:44,630 它看起来像我在某种循环的设置阵列[i]为0。 584 00:46:44,630 --> 00:46:50,510 它看起来像我从来没有打破这循环中。 585 00:46:50,510 --> 00:46:54,780 如果我打印我,所以我是2的话,我会去下。 586 00:46:54,780 --> 00:46:59,250 我要打印我,我是3,然后我会去下。 587 00:46:59,250 --> 00:47:05,360 我要打印我和我的3。下一步,打印我,我是4。 588 00:47:05,360 --> 00:47:14,520 实际上,打印sizeof(阵列)的,所以阵列的大小是20。 589 00:47:16,310 --> 00:47:32,870 但它看起来像有一些特殊的gdb命令下去,直到事情发生。 590 00:47:32,870 --> 00:47:37,620 这就像设置条件变量的值。但我不记得它是什么。 591 00:47:37,620 --> 00:47:44,100 因此,如果我们继续下去 - 592 00:47:44,100 --> 00:47:47,120 你刚才说什么?你带来了什么呢? 593 00:47:47,120 --> 00:47:50,500 [学生]不显示我添加 - “”是啊。因此,显示我可以帮忙。 594 00:47:50,500 --> 00:47:54,530 如果我们只是显示我会把这里i的值是什么 595 00:47:54,530 --> 00:47:56,470 所以我没有把它打印出来,每次。 596 00:47:56,470 --> 00:48:02,930 如果我们只是继​​续下一个,我们看到0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5。 597 00:48:02,930 --> 00:48:08,530 有些事情正在发生可怕的错误,而我正在被重置为0。 598 00:48:13,330 --> 00:48:22,220 在buggy4.c,我们看到的情况是int数组[5]; 599 00:48:22,220 --> 00:48:26,200 (i = 0; <= sizeof(数组)的,我+ +) 600 00:48:26,200 --> 00:48:28,550 阵列[i] = 0; 601 00:48:28,550 --> 00:48:31,390 这是错在这里,我们看到了什么? 602 00:48:31,390 --> 00:48:39,480 作为一个提示,当我在做gdb的buggy4 - 让我们将主要的运行 - 603 00:48:39,480 --> 00:48:45,980 我也只看到打印sizeof(数组)的条件是什么,我终于爆发。 604 00:48:47,690 --> 00:48:51,100 我在哪里?我跑了吗? 605 00:48:51,100 --> 00:48:54,280 我没有申报。 606 00:48:54,280 --> 00:48:58,680 因此,打印大小(数组)和20, 607 00:48:58,680 --> 00:49:06,690 这是意料之中的,因为我的数组的大小是5,它是由5个整数, 608 00:49:06,690 --> 00:49:12,410 所以,整个事情应该是5 * sizeof(int)的的字节,其中的sizeof(int)往往是4。 609 00:49:12,410 --> 00:49:14,780 因此,大小(阵列)是20。 610 00:49:14,780 --> 00:49:17,420 应该是什么内容呢? 611 00:49:17,420 --> 00:49:21,720 [学生]将是sizeof(int)。 “是啊,/是sizeof(int)。 612 00:49:21,720 --> 00:49:30,630 它看起来像有一个问题在这里。我想这应该是< 613 00:49:30,630 --> 00:49:36,960 因为它几乎总是<永不<=。 614 00:49:36,960 --> 00:49:44,860 现在,让我们来想想这是为什么居然断。 615 00:49:44,860 --> 00:49:53,370 没有任何人有猜测为什么我重置为0到每个迭代循环吗? 616 00:50:01,300 --> 00:50:09,350 内这里发生的事情是,唯一阵列[i]被设置为0。 617 00:50:09,350 --> 00:50:15,350 不知何故,这行代码导致我们的诠释,我将其设置为0。 618 00:50:16,730 --> 00:50:23,130 [学生]难道是我这部分的记忆,因为它的首要 619 00:50:23,130 --> 00:50:27,970 当它认为它的下一个元素的数组? >> [鲍登]是的。 620 00:50:27,970 --> 00:50:33,880 当我们超越我们的阵列, 621 00:50:33,880 --> 00:50:39,870 不知何故,我们覆盖的空间,是压倒一切的我的价值。 622 00:50:39,870 --> 00:50:48,030 所以,如果我们看一下为buggy4,打破主体,运行, 623 00:50:48,030 --> 00:50:53,120 让我们打印i的地址。 624 00:50:53,120 --> 00:50:57,280 看起来就像是bffff124。 625 00:50:57,280 --> 00:51:03,930 现在,让我们打印的地址的数组[0]。 110。 626 00:51:03,930 --> 00:51:06,290 怎么样[1]? 114。 627 00:51:06,290 --> 00:51:07,920 [2],118。 628 00:51:07,920 --> 00:51:14,530 11c中,120。阵列[5] bfff124。 629 00:51:14,530 --> 00:51:26,990 因此,阵列[5],因为我,这意味着该数组[5]我有相同的地址。 630 00:51:26,990 --> 00:51:30,720 如果他们有相同的地址,他们是同样的事情。 631 00:51:30,720 --> 00:51:38,410 所以,当我们阵列[5]为0,我们正在设置i为0。 632 00:51:38,410 --> 00:51:46,070 如果您认为有关这方面的堆栈, 633 00:51:46,070 --> 00:51:55,590 诠释我首先声明,这意味着我得到了一些堆栈空间。 634 00:51:55,590 --> 00:52:04,730 阵列[5],20个字节被分配在栈上分配。 635 00:52:04,730 --> 00:52:08,400 所以,我首先被分配,那么这20个字节被分配。 636 00:52:08,400 --> 00:52:11,400 所以,我前阵发生, 637 00:52:11,400 --> 00:52:19,230 因为,就像我上周表示,在技术堆栈向下增长的, 638 00:52:19,230 --> 00:52:28,520 当你对数组的索引,我们也能保证这个数组中的第0个位置 639 00:52:28,520 --> 00:52:31,970 总是发生之前,数组中的第一个位置。 640 00:52:31,970 --> 00:52:35,900 这是我画了上周的种。 641 00:52:35,900 --> 00:52:42,210 请注意,在底部我们的地址为0,并在顶部,我们有地址最大。 642 00:52:42,210 --> 00:52:44,880 该协议栈始终是越来越大了下来。 643 00:52:48,100 --> 00:52:53,500 比方说,我们分配给我。 644 00:52:53,500 --> 00:52:59,680 我们分配整数i,这意味着我们只能说,这里整数,我被分配。 645 00:52:59,680 --> 00:53:06,420 然后我们分配了5个整数组成的数组,这意味着底下的, 646 00:53:06,420 --> 00:53:11,230 由于堆栈的增长,这5个整数如何分配。 647 00:53:11,230 --> 00:53:15,900 但是,由于阵列是如何工作的,我们保证在数组中的第一个位置 648 00:53:15,900 --> 00:53:22,260 总是有的地址小于数组中的第二件事情。 649 00:53:22,260 --> 00:53:28,270 因此,数组中的位置始终为0,首先发生在内存中, 650 00:53:28,270 --> 00:53:30,700 而数组中的位置发生后, 651 00:53:30,700 --> 00:53:33,310 数组中的位置2具有发生之后, 652 00:53:33,310 --> 00:53:37,900 这意味着,数组中的位置0将发生的地方,在这里, 653 00:53:37,900 --> 00:53:40,690 数组中的位置会发生以上, 654 00:53:40,690 --> 00:53:45,530 因为移动意味着更高的地址以来的最高地址是在这里。 655 00:53:45,530 --> 00:53:50,490 所以数组[0]在这里,数组[1]起来这里,阵列[2]起来这里,阵列[3]在这里。 656 00:53:50,490 --> 00:53:55,620 请注意之前,我们如何分配的整数,我在这里, 657 00:53:55,620 --> 00:54:01,040 随着我们进一步到我们的数组,我们正在越来越接近我们的整数i。 658 00:54:01,040 --> 00:54:07,640 碰巧的是该数组[5],这是超出了我们的数组中的一个位置, 659 00:54:07,640 --> 00:54:13,010 正是在那里我正好是整数分配。 660 00:54:13,010 --> 00:54:16,920 所以,这点,我们碰巧被击中的堆栈空间 661 00:54:16,920 --> 00:54:21,680 被分配的整数i,我们设置为0。 662 00:54:21,680 --> 00:54:26,160 >> 这是如何工作的。有问题吗?是啊。 663 00:54:26,160 --> 00:54:30,710 [学生]不要介意。好吧。 664 00:54:30,710 --> 00:54:33,090 [学生]:你如何避免这些类型的错误呢? 665 00:54:33,090 --> 00:54:41,190 这些排序的错误?不要使用C语言作为编程语言。 666 00:54:41,190 --> 00:54:45,840 使用一种语言,数组边界检查。 667 00:54:45,840 --> 00:54:55,900 只要你小心,你只需要你的数组的边界,以避免过去。 668 00:54:55,900 --> 00:54:58,300 [学生]所以,在这里,当我们走过你的数组的边界 - 669 00:54:58,300 --> 00:55:01,840 鲍登这就是事情开始出错。 >> [学生]:哦,好吧。 670 00:55:01,840 --> 00:55:05,730 只要你留在分配的内存为您的阵列,你的罚款。 671 00:55:05,730 --> 00:55:12,400 但是C没有错误检查。如果我这样做阵列[1000],它会很高兴地只需要修改无论发生什么事 - 672 00:55:12,400 --> 00:55:16,500 它进入阵列的开头,然后它进入1000位置后,将其设置为0。 673 00:55:16,500 --> 00:55:20,000 它没有做任何检查,哦,这不居然有1000的东西在里面。 674 00:55:20,000 --> 00:55:22,750 1000是远远超出我应该改变什么, 675 00:55:22,750 --> 00:55:26,940 而Java或什么的,你会得到数组的界限指标 676 00:55:26,940 --> 00:55:29,820 或指数范围异常。 677 00:55:29,820 --> 00:55:33,950 这就是为什么有很多的高级语言有这些东西 678 00:55:33,950 --> 00:55:37,340 如果你超越了数组界限,你失败了 679 00:55:37,340 --> 00:55:40,070 所以,你不能改变的东西从下面你 680 00:55:40,070 --> 00:55:42,590 事情就远不如刚刚得到一个异常 681 00:55:42,590 --> 00:55:44,940 他说,你去结束之后的数组。 682 00:55:44,940 --> 00:55:50,970 [学生]因此,我们应该只是改变了<=只是<? >> [鲍登]是啊。 683 00:55:50,970 --> 00:55:54,800 它应该是“大小(数组)/ sizeof(廉政); 684 00:55:54,800 --> 00:55:59,560 大小(数组)是20,但我们只希望5。 >> [学生]。 685 00:55:59,560 --> 00:56:04,060 还有问题吗?好吧。 686 00:56:04,060 --> 00:56:07,380 >> [学生]:我有一个问题。 >>呀。 687 00:56:07,380 --> 00:56:16,440 [学生]:什么是实际的数组变量? 688 00:56:16,440 --> 00:56:20,000 [鲍登]究竟什么是数组吗? 689 00:56:20,000 --> 00:56:24,930 数组本身是一个符号。 690 00:56:24,930 --> 00:56:31,490 这仅仅是我们所引用的20个字节的开始地址。 691 00:56:31,490 --> 00:56:38,070 你可以认为它是一个指针,但它是一个常量指针。 692 00:56:38,070 --> 00:56:44,140 只要东西被编译,变量数组不存在了。 693 00:56:44,140 --> 00:56:48,210 [学生]那么,它是如何找到数组的大小吗? 694 00:56:48,210 --> 00:56:54,130 数组的大小是指,该块的大小,符号是指。 695 00:56:54,130 --> 00:57:01,240 当我做的东西像printf(“%p \ n”,阵列); 696 00:57:01,240 --> 00:57:05,140 让我们来运行它。 697 00:57:12,960 --> 00:57:15,530 我刚刚做了什么错了吗? 698 00:57:15,530 --> 00:57:19,220 阵列的阵列“在这里声明。 699 00:57:20,820 --> 00:57:23,200 哦,在这里。 700 00:57:23,200 --> 00:57:31,250 铛是聪明的,和它发生,请注意,我宣布5个元素的数组作为 701 00:57:31,250 --> 00:57:34,540 但我索引到位置1000。 702 00:57:34,540 --> 00:57:38,450 它可以这样做,因为这些都只是常量。 703 00:57:38,450 --> 00:57:43,370 它只能走这么远,我注意到,超出了数组界限。 704 00:57:43,370 --> 00:57:46,880 但是请注意,当我们有我是不正确的, 705 00:57:46,880 --> 00:57:51,040 ,它可能无法确定我能承担多少值, 706 00:57:51,040 --> 00:57:55,540 所以也不能确定,我打算结束之后的数组。 707 00:57:55,540 --> 00:57:59,430 这只是铛聪明的。 708 00:57:59,430 --> 00:58:03,340 >> 但现在buggy4。那么,还有什么我做错了什么? 709 00:58:03,340 --> 00:58:05,970 库函数printf的'隐式声明。 710 00:58:05,970 --> 00:58:14,960 我想#包括的。 711 00:58:14,960 --> 00:58:18,710 好吧。现在运行buggy4。 712 00:58:18,710 --> 00:58:24,840 打印值的数组像我在这里做的,打印出来作为一个指针 713 00:58:24,840 --> 00:58:30,060 打印出一些东西,看起来像这样 - bfb8805c - 这是一些地址 714 00:58:30,060 --> 00:58:33,450 这是在堆栈上下的区域。 715 00:58:33,450 --> 00:58:41,820 数组本身是这样一个指针,但它不是一个实际的指针, 716 00:58:41,820 --> 00:58:45,410 因为常规的指针,我们可以改变的。 717 00:58:45,410 --> 00:58:54,700 阵列仅仅是一些常数。 20块的开始在地址0xbfb8805c的内存。 718 00:58:54,700 --> 00:59:09,020 所以bfb8805c通过这个地址+20或我猜-20 - 719 00:59:09,020 --> 00:59:17,400 这个数组分配的内存。 720 00:59:17,400 --> 00:59:20,350 数组,变量本身不存储任何地方。 721 00:59:20,350 --> 00:59:27,660 当你在编译时,编译器 - 手一挥 - 722 00:59:27,660 --> 00:59:33,060 但编译器将只使用它知道数组是。 723 00:59:33,060 --> 00:59:36,090 它知道该数组的开始, 724 00:59:36,090 --> 00:59:40,910 ,因此它可以做的事情在一定的偏移量从这个开始。 725 00:59:40,910 --> 00:59:43,960 它并不需要一个变量本身代表阵列。 726 00:59:43,960 --> 00:59:53,730 但是,当我做一些事情,如int * p =阵列,p是一个指针,它指向该数组, 727 00:59:53,730 --> 00:59:57,830 现在p实际上不存在在栈上。 728 00:59:57,830 --> 01:00:01,950 我将p。我可以做p = malloc的。 729 01:00:01,950 --> 01:00:06,500 因此,它最初指向阵列,现在它指向的堆一些空间。 730 01:00:06,500 --> 01:00:09,620 我不能这样做阵列= malloc的。 731 01:00:09,620 --> 01:00:13,710 如果铛是聪明的,它会冲我喊了蝙蝠的权利。 732 01:00:17,000 --> 01:00:21,430 其实,我敢肯定,海湾合作委员会将做到这一点。 733 01:00:21,430 --> 01:00:25,010 因此,数组类型为int [5]“是不可转让的。 734 01:00:25,010 --> 01:00:28,040 您无法分配到一个数组类型的东西 735 01:00:28,040 --> 01:00:30,500 因为数组是一个常数。 736 01:00:30,500 --> 01:00:34,760 这是一个符号引用这20个字节。我不能改变它。 737 01:00:34,760 --> 01:00:37,690 >> [学生]数组的大小是存储在哪里? 738 01:00:37,690 --> 01:00:40,670 鲍登这不是存储在任何位置。这时候,它的编译。 739 01:00:40,670 --> 01:00:46,310 那么,是存储数组的大小? 740 01:00:46,310 --> 01:00:51,870 您可以使用sizeof(数组)的函数,数组声明本身的内部。 741 01:00:51,870 --> 01:01:03,150 所以,如果我做了一些功能,foo和我这样做(int数组[]) 742 01:01:03,150 --> 01:01:10,450 输出(“%d \ n”,sizeof(数组)的); 743 01:01:10,450 --> 01:01:21,330 然后,我在这里调用foo(数组); 744 01:01:21,330 --> 01:01:24,840 里面的这个功能 - 让我们来运行它。 745 01:01:34,200 --> 01:01:36,840 这是再聪明的铿锵。 746 01:01:36,840 --> 01:01:43,890 它告诉我的sizeof数组函数参数 747 01:01:43,890 --> 01:01:46,690 将返回了'int *'的大小。 748 01:01:46,690 --> 01:01:55,150 这将是一个错误,如果它不是我希望发生的。 749 01:01:55,150 --> 01:01:58,960 关闭Werror。 750 01:02:14,950 --> 01:02:17,590 警告。警告罚款。 751 01:02:17,590 --> 01:02:19,960 它仍然会编译,只要它有一个警告。 752 01:02:19,960 --> 01:02:22,910 。/ a.out的打印4。 753 01:02:22,910 --> 01:02:28,650 生成的警告,是一个明确的指标,出了什么问题。 754 01:02:28,650 --> 01:02:34,120 int数组只是要打印的大小(*)。 755 01:02:34,120 --> 01:02:39,790 即使我把阵列[5]在这里,它仍然只是要打印的大小(*)。 756 01:02:39,790 --> 01:02:47,440 因此,只要你把它传递到一个函数,数组和指针的区别 757 01:02:47,440 --> 01:02:49,670 是不存在的。 758 01:02:49,670 --> 01:02:52,640 这恰好是一个数组,在堆栈上声明, 759 01:02:52,640 --> 01:02:58,300 但只要我们把这个值,0xbf胡说,胡说,胡说到这个函数, 760 01:02:58,300 --> 01:03:03,350 那么这个指针指向该数组在堆栈中。 761 01:03:03,350 --> 01:03:08,310 因此,这意味着,大小只适用于被宣布的阵列的功能, 762 01:03:08,310 --> 01:03:11,230 这意味着,当你在编译此功能, 763 01:03:11,230 --> 01:03:17,330 铛“时,通过这个功能,它认为数组是一个int数组大小为5。 764 01:03:17,330 --> 01:03:20,640 那么它看到的大小(数组)。嗯,这是20。 765 01:03:20,640 --> 01:03:26,440 这实际上是大小基本上几乎所有的情况下。 766 01:03:26,440 --> 01:03:31,150 sizeof是一个函数,它是一个运营商。 767 01:03:31,150 --> 01:03:33,570 你不调用sizeof函数。 768 01:03:33,570 --> 01:03:38,280 如sizeof(int),编译器会翻译到4。 769 01:03:41,480 --> 01:03:43,700 明白了吗?好吧。 770 01:03:43,700 --> 01:03:47,520 >> [学生]那么,什么是大小(阵列)之间的区别主要在富? 771 01:03:47,520 --> 01:03:52,840 这是因为我们说,这是int *类型的sizeof(数组)的, 772 01:03:52,840 --> 01:03:57,120 而数组在这里是不是int *类型,它是一个int数组。 773 01:03:57,120 --> 01:04:04,540 >> [学生]所以,如果你有参数的数组[],而不是int *数组, 774 01:04:04,540 --> 01:04:09,230 这意味着,你仍然可以改变,因为现在它是一个指针数组吗? 775 01:04:09,230 --> 01:04:14,250 鲍登]像这样吗? >> [学生]是啊。你可以改变数组内的功能呢? 776 01:04:14,250 --> 01:04:18,420 鲍登在这两种情况下,你可以改变阵列。 777 01:04:18,420 --> 01:04:23,130 在这两种情况下,你是说数组[4] = 0。 778 01:04:23,130 --> 01:04:26,590 [学生]但你可以使阵列点别的东西吗? 779 01:04:26,590 --> 01:04:30,230 鲍登哦。是啊。在这两种情况下 - >> [学生]是啊。 780 01:04:30,230 --> 01:04:38,410 鲍登]数组[]和int *数组之间的区别,是没有的。 781 01:04:38,410 --> 01:04:42,570 在这里,您还可以得到一些多维数组 782 01:04:42,570 --> 01:04:47,050 一些方便的语法,但它仍然只是一个指针。 783 01:04:47,050 --> 01:04:56,400 这意味着,我可以自由地做阵列的malloc(如sizeof(int));现在指向别的地方。 784 01:04:56,400 --> 01:04:59,610 但就像是如何工作的,永远总是, 785 01:04:59,610 --> 01:05:03,210 改变这种阵列,使其指向其他的东西 786 01:05:03,210 --> 01:05:07,570 不改变这个数组,在这里,因为它是一个副本的说法, 787 01:05:07,570 --> 01:05:10,780 它不是一个指针,这样的说法。 788 01:05:10,780 --> 01:05:16,070 而实际上,随着越来越多的迹象表明,它是完全一样的 - 789 01:05:16,070 --> 01:05:21,100 我们已经看到了印刷阵列打印 - 790 01:05:21,100 --> 01:05:31,410 如果我们打印的地址数组的数组的地址或地址 791 01:05:31,410 --> 01:05:36,290 无论是那些? 792 01:05:41,770 --> 01:05:45,220 让我们忽略这一项。 793 01:05:48,140 --> 01:05:51,660 好吧。这是罚款。现在,运行。/ a.out的。 794 01:05:51,660 --> 01:06:00,220 印刷阵列,然后打印该数组的地址,同样的事情。 795 01:06:00,220 --> 01:06:02,870 阵列根本就不存在。 796 01:06:02,870 --> 01:06:08,190 它知道,当你打印数组,你要打印的符号,指的是那些20个字节。 797 01:06:08,190 --> 01:06:11,940 好,打印的地址的数组,数组的操作是不存在的。 798 01:06:11,940 --> 01:06:17,200 它不会有一个地址,所以它只是打印这20个字节的地址。 799 01:06:20,820 --> 01:06:28,150 只要你编译,想在你的编译buggy4。/ a.out格式, 800 01:06:28,150 --> 01:06:30,340 数组是不存在的。 801 01:06:30,340 --> 01:06:33,640 指针存在。数组没有。 802 01:06:34,300 --> 01:06:38,060 代表该数组的内存块仍然存在, 803 01:06:38,060 --> 01:06:43,270 但该类型的变量数组和变量不存在。 804 01:06:46,260 --> 01:06:50,270 这些都是像数组和指针之间的主要区别 805 01:06:50,270 --> 01:06:55,590 只要你调用函数,没有任何区别。 806 01:06:55,590 --> 01:07:00,460 但里面被声明为数组本身的功能,大小不同的方式工作 807 01:07:00,460 --> 01:07:05,190 因为你打印的类型的大小,而不是块的大小, 808 01:07:05,190 --> 01:07:08,950 你不能改变它,因为它是一个符号。 809 01:07:08,950 --> 01:07:14,370 打印的东西打印的东西和地址同样的事情。 810 01:07:14,370 --> 01:07:18,480 而这几乎是它。 811 01:07:18,480 --> 01:07:20,820 [学生]你能说更多的时间吗? 812 01:07:21,170 --> 01:07:24,170 我可能错过了一些东西。 813 01:07:24,170 --> 01:07:29,260 印刷的数组的数组和地址打印同样的事情, 814 01:07:29,260 --> 01:07:33,180 而如果你打印的指针与指针​​的地址, 815 01:07:33,180 --> 01:07:36,010 打印一件事你指向的地址, 816 01:07:36,010 --> 01:07:40,360 其他打印的指针在栈上的地址。 817 01:07:40,360 --> 01:07:47,040 你可以改变一个指针,你不能改变数组的象征。 818 01:07:47,740 --> 01:07:53,270 和sizeof指针要打印该指针类型的大小。 819 01:07:53,270 --> 01:07:57,470 所以int * p的大小(P),打印4张, 820 01:07:57,470 --> 01:08:04,110 但int数组[5]打印sizeof(数组)的打印20。 821 01:08:04,110 --> 01:08:07,480 [学生] int数组[5]将打印20? “是的。 822 01:08:07,480 --> 01:08:13,300 这就是为什么内的buggy4的时候,它使用的是大小(阵列) 823 01:08:13,300 --> 01:08:16,660 这是我做<20,这是不是我们想要的。 824 01:08:16,660 --> 01:08:20,880 我们希望I <5。 >> [学生]好吧。 825 01:08:20,880 --> 01:08:25,569 [鲍登然后只要你开始传递的功能, 826 01:08:25,569 --> 01:08:34,340 如果我们这样做* P =阵列; 827 01:08:34,340 --> 01:08:39,779 这个函数里面,我们基本上可以使用p和阵列完全相同的方式, 828 01:08:39,779 --> 01:08:43,710 除了的sizeof问题和不断变化的问题​​。 829 01:08:43,710 --> 01:08:49,810 但是p [0] = 1;说阵列[0] = 1是相同的; 830 01:08:49,810 --> 01:08:55,600 只要我们说富(数组)或者foo(P); 831 01:08:55,600 --> 01:08:59,760 里面的函数foo,这是两次相同的呼叫。 832 01:08:59,760 --> 01:09:03,350 这两个调用之间没有任何区别。 833 01:09:07,029 --> 01:09:11,080 >> 每个人都好?好吧。 834 01:09:14,620 --> 01:09:17,950 我们有10分钟。 835 01:09:17,950 --> 01:09:28,319 >> 我们将尝试拿到通过这个黑客打字员程序, 836 01:09:28,319 --> 01:09:32,350 本网站,去年或什么的就出来了。 837 01:09:34,149 --> 01:09:41,100 这只是应该像你输入随机它打印出来 - 838 01:09:41,100 --> 01:09:46,729 无论它发生在已加载的文件是什么,它​​看起来像你打字。 839 01:09:46,729 --> 01:09:52,069 它看起来像某种操作系统代码。 840 01:09:53,760 --> 01:09:56,890 这就是我们要实现的。 841 01:10:08,560 --> 01:10:11,690 你应该有一个二进制可执行文件hacker_typer 842 01:10:11,690 --> 01:10:14,350 是发生在一个单一的参数,“黑客类型的文件。” 843 01:10:14,350 --> 01:10:16,480 运行可执行程序,应清除屏幕 844 01:10:16,480 --> 01:10:20,850 然后打印出传入的文件,每次用户按下一个键一个字符。 845 01:10:20,850 --> 01:10:24,990 所以,你按什么键,它应该扔掉,而不是从文件打印字符 846 01:10:24,990 --> 01:10:27,810 这是参数。 847 01:10:29,880 --> 01:10:34,350 我几乎会告诉你,我们需要知道的事情是什么。 848 01:10:34,350 --> 01:10:36,440 但是,我们要检查的termios库。 849 01:10:36,440 --> 01:10:44,840 在我的整个生活,我从来没有使用这个库,所以它具有非常小的目的。 850 01:10:44,840 --> 01:10:48,610 但是,这将是图书馆内,我们可以用它来扔掉你打的字符 851 01:10:48,610 --> 01:10:52,390 当你输入到标准英寸 852 01:10:56,970 --> 01:11:05,840 所以hacker_typer.c,我们会想包括的。 853 01:11:05,840 --> 01:11:12,870 的手册页的termios的 - 我猜测它的终端OS或东西 - 854 01:11:12,870 --> 01:11:16,240 我不知道如何读它。 855 01:11:16,240 --> 01:11:21,040 在此,它说,包括这2个文件,所以我们将做到这一点。 856 01:11:37,620 --> 01:11:46,820 >> 第一件事,第一,我们要在一个单一的说法,这是我们要打开的文件。 857 01:11:46,820 --> 01:11:52,420 那么,我该怎么办?我该如何检查,看看我有一个参数呢? 858 01:11:52,420 --> 01:11:56,480 [学生],如果argc等于它。 >> [鲍登]是啊。 859 01:11:56,480 --> 01:12:21,250 所以,如果(ARGC = 2)输出(“用法:%s [文件打开]”)。 860 01:12:21,250 --> 01:12:32,750 所以,现在如果我没有提供第二个参数 - 哦,我需要新的生产线 - 861 01:12:32,750 --> 01:12:36,240 你会看到它说,使用/ hacker_typer, 862 01:12:36,240 --> 01:12:39,770 第二个参数应该是我要打开的文件。 863 01:12:58,430 --> 01:13:01,260 现在我该怎么办? 864 01:13:01,260 --> 01:13:08,490 我想从这个文件中读取。我如何从文件中读取吗? 865 01:13:08,490 --> 01:13:11,920 [学生]:你先打开它。 >>呀。 866 01:13:11,920 --> 01:13:15,010 所以FOPEN。 FOPEN看是什么样的? 867 01:13:15,010 --> 01:13:22,980 [学生]文件名。 >> [鲍登] filename是要ARGV [1]。 868 01:13:22,980 --> 01:13:26,110 [学生],然后你想用它做什么,所以 - >> [鲍登]是啊。 869 01:13:26,110 --> 01:13:28,740 所以,如果你不记得,你可能只是做男人FOPEN, 870 01:13:28,740 --> 01:13:32,960 它的将是一个const char * path的路径是文件名, 871 01:13:32,960 --> 01:13:34,970 为const char *模式。 872 01:13:34,970 --> 01:13:38,660 如果你碰巧不记得是什么模式,那么你可以看看模式。 873 01:13:38,660 --> 01:13:44,660 里面的手册页,斜线字符是什么,你可以用它来搜索东西。 874 01:13:44,660 --> 01:13:49,790 所以,我键入搜索模式/模式。 875 01:13:49,790 --> 01:13:57,130 N和N是什么,你可以用它来通过搜索匹配周期。 876 01:13:57,130 --> 01:13:59,800 这里说的参数模式指向一个字符串 877 01:13:59,800 --> 01:14:01,930 开始与下列序列之一。 878 01:14:01,930 --> 01:14:06,480 所以,R,打开文本文件进行读取。这就是我们想要做的。 879 01:14:08,930 --> 01:14:13,210 对于阅读,我要存储。 880 01:14:13,210 --> 01:14:18,720 这件事是怎么回事,是一个FILE *。现在我想要做的是什么呢? 881 01:14:18,720 --> 01:14:21,200 给了我第二次​​。 882 01:14:28,140 --> 01:14:30,430 好吧。现在我想要做的是什么呢? 883 01:14:30,430 --> 01:14:32,940 [学生]检查它是否为NULL。 >> [鲍登]是啊。 884 01:14:32,940 --> 01:14:38,690 任何时候你打开一个文件时,请确保你能够成功打开它。 885 01:14:58,930 --> 01:15:10,460 >> 现在我想做的事,termios的东西,我想先读我的当前设置 886 01:15:10,460 --> 01:15:14,050 并保存到的东西,那么我想改变我的设置 887 01:15:14,050 --> 01:15:19,420 扔掉我键入任何字符, 888 01:15:19,420 --> 01:15:22,520 然后我想更新这些设置。 889 01:15:22,520 --> 01:15:27,250 然后在程序结束时,我想改回我原来的设置。 890 01:15:27,250 --> 01:15:32,080 结构类型termios的,我会想这些。 891 01:15:32,080 --> 01:15:35,600 第一个是是我的current_settings, 892 01:15:35,600 --> 01:15:42,010 然后,他们将是我的hacker_settings。 893 01:15:42,010 --> 01:15:48,070 首先,我要保存我的当前设置, 894 01:15:48,070 --> 01:15:53,790 然后我会要更新hacker_settings, 895 01:15:53,790 --> 01:16:01,570 然后结束时,我的计划,我想恢复到当前设置。 896 01:16:01,570 --> 01:16:08,660 所以保存当前设置的方式工作,我们的人termios的。 897 01:16:08,660 --> 01:16:15,810 我们看到,我们有这样的诠释tcsetattr,诠释tcgetattr。 898 01:16:15,810 --> 01:16:22,960 我通过一个termios结构由它的指针。 899 01:16:22,960 --> 01:16:30,640 这看起来是 - 我已经忘记了调用该函数。 900 01:16:30,640 --> 01:16:34,930 复制和粘贴。 901 01:16:39,150 --> 01:16:45,500 因此,tcgetattr,然后我保存的信息,我想通过在结构中, 902 01:16:45,500 --> 01:16:49,650 这是怎么回事是current_settings, 903 01:16:49,650 --> 01:16:59,120 而第一个参数是文件描述符的事情,我要保存的属性。 904 01:16:59,120 --> 01:17:04,360 任何时候你打开一个文件的文件描述符是,它得到一个文件描述符。 905 01:17:04,360 --> 01:17:14,560 当我FOPEN的argv [1],它得到了你所引用的文件描述符 906 01:17:14,560 --> 01:17:16,730 每当你要读取或写入到它。 907 01:17:16,730 --> 01:17:19,220 这不是我想在这里使用的文件描述符。 908 01:17:19,220 --> 01:17:21,940 默认情况下,你有三个文件描述符, 909 01:17:21,940 --> 01:17:24,310 这是标准输入,标准输出和标准错误。 910 01:17:24,310 --> 01:17:29,960 默认情况下,我认为它的标准是0,标准输出为1,标准误差为2。 911 01:17:29,960 --> 01:17:33,980 那么,我要更改的设置吗? 912 01:17:33,980 --> 01:17:37,370 我想改变的设置,每当我打了一个字符, 913 01:17:37,370 --> 01:17:41,590 我希望它抛出的字符,而不是打印到屏幕上。 914 01:17:41,590 --> 01:17:45,960 流 - 标准,标准输出和标准错误 - 915 01:17:45,960 --> 01:17:52,050 响应的事情,当我在键盘上键入? >> [学生]标准英寸>>呀。 916 01:17:52,050 --> 01:17:56,450 所以我可以做0或者我可以做标准输入。 917 01:17:56,450 --> 01:17:59,380 我得到了current_settings标准中。 918 01:17:59,380 --> 01:18:01,720 >> 现在,我想更新这些设置, 919 01:18:01,720 --> 01:18:07,200 所以,我首先复制到hacker_settings我的current_settings的。 920 01:18:07,200 --> 01:18:10,430 结构如何工作,它只会复制。 921 01:18:10,430 --> 01:18:14,510 这会将所有的领域,如你所期望的。 922 01:18:14,510 --> 01:18:17,410 >> 现在,我要更新的一些字段。 923 01:18:17,410 --> 01:18:21,670 在termios的,你就必须读了很多这方面 924 01:18:21,670 --> 01:18:24,110 只是看你要寻找的, 925 01:18:24,110 --> 01:18:28,210 但标志,你会想看看有回音, 926 01:18:28,210 --> 01:18:33,110 所以echo回显输入的字符。 927 01:18:33,110 --> 01:18:37,710 首先,我想设置 - 从来就已经被遗忘的领域是什么。 928 01:18:45,040 --> 01:18:47,900 这是结构看起来像什么。 929 01:18:47,900 --> 01:18:51,060 所以输入模式,我觉得我们要改变。 930 01:18:51,060 --> 01:18:54,210 我们来看看解决方案,以确保这正是我们想要改变。 931 01:19:04,060 --> 01:19:12,610 我们要改变lflag,以防止需要通过所有这些。 932 01:19:12,610 --> 01:19:14,670 我们要改变本地模式。 933 01:19:14,670 --> 01:19:17,710 你将不得不读通过这整个事情了解的一切都属于 934 01:19:17,710 --> 01:19:19,320 我们要改变。 935 01:19:19,320 --> 01:19:24,120 但是它里面的本地模式,我们要去的地方,要改变这种状况。 936 01:19:27,080 --> 01:19:33,110 因此,hacker_settings.cc_lmode是它叫什么。 937 01:19:39,630 --> 01:19:43,020 c_lflag。 938 01:19:49,060 --> 01:19:52,280 这是我们进入按位运算符。 939 01:19:52,280 --> 01:19:54,860 我们种出来的时候,但我们会通过它真正的快速。 940 01:19:54,860 --> 01:19:56,600 这是我们进入按位运算符, 941 01:19:56,600 --> 01:19:59,950 在那里我觉得我说的很久以前,每当你开始处理标志, 942 01:19:59,950 --> 01:20:03,370 你将要使用按位运算符有很多。 943 01:20:03,370 --> 01:20:08,240 标志中的每个位对应于某种行为。 944 01:20:08,240 --> 01:20:14,090 所以在这里,这个标志有一堆不同的东西,所有的人意味着不同的东西。 945 01:20:14,090 --> 01:20:18,690 但我想要做的是关闭位,对应于ECHO。 946 01:20:18,690 --> 01:20:25,440 因此,要打开关闭我做&=¬ECHO。 947 01:20:25,440 --> 01:20:30,110 其实,我认为这是如TECHO什么。我只是要再次检查。 948 01:20:30,110 --> 01:20:34,050 我可以termios的。这只是回声。 949 01:20:34,050 --> 01:20:38,440 ECHO将是一个单比特。 950 01:20:38,440 --> 01:20:44,230 ,ECHO将意味着所有位都设置为1,这意味着所有的标志都设置为true 951 01:20:44,230 --> 01:20:47,140 除了的ECHO位。 952 01:20:47,140 --> 01:20:53,830 结束我的本地标志,这意味着目前所有标志设置为true, 953 01:20:53,830 --> 01:20:56,520 将仍然被设置为true。 954 01:20:56,520 --> 01:21:03,240 如果我的ECHO标志设置为true,那么这是必然的ECHO标志设置为false。 955 01:21:03,240 --> 01:21:07,170 所以,这行代码只是关闭ECHO标志。 956 01:21:07,170 --> 01:21:16,270 行代码,我就复制他们的利益的时间,然后加以解释。 957 01:21:27,810 --> 01:21:30,180 在该解决方案中,他说0。 958 01:21:30,180 --> 01:21:33,880 明确地说,标准输入,它可能会更好。 959 01:21:33,880 --> 01:21:42,100 >> 请注意,我也做ECHO ICANON。 960 01:21:42,100 --> 01:21:46,650 ICANON是指一些独立的,这意味着典型的模式。 961 01:21:46,650 --> 01:21:50,280 典型模式是指的是当你键入的命令行, 962 01:21:50,280 --> 01:21:54,670 标准不处理任何事情,直到你击中换行。 963 01:21:54,670 --> 01:21:58,230 所以当你的GetString,你键入了一堆东西,然后你打换行。 964 01:21:58,230 --> 01:22:00,590 这时候,它的发送到标准中。 965 01:22:00,590 --> 01:22:02,680 这是默认的。 966 01:22:02,680 --> 01:22:05,830 当我关闭典型模式,你按现在每一个字符 967 01:22:05,830 --> 01:22:10,910 是被处理,这通常是有种不好的,因为它是缓慢的处理这些事情, 968 01:22:10,910 --> 01:22:14,330 这就是为什么它是很好的缓冲到整行。 969 01:22:14,330 --> 01:22:16,810 但我想每个字符处理 970 01:22:16,810 --> 01:22:18,810 因为我不希望它等待我打换行 971 01:22:18,810 --> 01:22:21,280 前处理所有的字符,我一直打字。 972 01:22:21,280 --> 01:22:24,760 关闭标准模式。 973 01:22:24,760 --> 01:22:31,320 这东西只是意味着当实际处理字符。 974 01:22:31,320 --> 01:22:35,830 这意味着,他们立即处理;尽快,因为我打字,处理它们。 975 01:22:35,830 --> 01:22:42,510 这是更新的功能,我的设置标准, 976 01:22:42,510 --> 01:22:45,480 和TCSA手段做是正确的。 977 01:22:45,480 --> 01:22:50,310 其他选项等待,,直到流上的一切,这是目前处理。 978 01:22:50,310 --> 01:22:52,030 这其实并不重要。 979 01:22:52,030 --> 01:22:56,920 现在改变我的设置,无论是目前在hacker_typer_settings的。 980 01:22:56,920 --> 01:23:02,210 我想我把它叫做hacker_settings,让我们改变它。 981 01:23:09,610 --> 01:23:13,500 改变一切hacker_settings。 982 01:23:13,500 --> 01:23:16,870 >> 现在,在我们的计划,我们将要恢复 983 01:23:16,870 --> 01:23:20,210 什么是目前的normal_settings内, 984 01:23:20,210 --> 01:23:26,560 这是怎么回事,只是看起来像及normal_settings。 985 01:23:26,560 --> 01:23:30,650 请注意,我并​​没有改变任何,我normal_settings,因为最初得到它。 986 01:23:30,650 --> 01:23:34,520 然后就改回来,我通过他们在最后。 987 01:23:34,520 --> 01:23:38,390 这是更新。好吧。 988 01:23:38,390 --> 01:23:43,900 >> 内,在这里我就解释中的代码的利益。 989 01:23:43,900 --> 01:23:46,350 这是没有那么多的代码。 990 01:23:50,770 --> 01:24:03,750 我们看到,我们从文件中读取一个字符。我们称它f。 991 01:24:03,750 --> 01:24:07,850 现在,您可以男人fgetc函数,fgetc去上班 992 01:24:07,850 --> 01:24:11,910 只是它会返回你刚才读的字符或EOF, 993 01:24:11,910 --> 01:24:15,680 其对应的端部的文件或一些错误发生。 994 01:24:15,680 --> 01:24:19,900 我们循环,继续从文件中读取一个字符, 995 01:24:19,900 --> 01:24:22,420 直到我们已经用完了要读取的字符。 996 01:24:22,420 --> 01:24:26,650 而我们这样做,我们等待的单个字符标准英寸 997 01:24:26,650 --> 01:24:29,090 每一次你在命令行中键入一些东西, 998 01:24:29,090 --> 01:24:32,820 阅读一个字符从标准中。 999 01:24:32,820 --> 01:24:38,330 随后的putchar只是打算把在这里,我们读到了从文件到标准输出的字符。 1000 01:24:38,330 --> 01:24:42,890 男人的putchar,但它只是把输出到标准输出,它的打印字符。 1001 01:24:42,890 --> 01:24:51,600 你也可以做输出(“%C”,C);同样的想法。 1002 01:24:53,330 --> 01:24:56,670 这是怎么回事,做大量的工作。 1003 01:24:56,670 --> 01:25:00,300 >> 我们想要做的最后一件事是只是FCLOSE我们的文件。 1004 01:25:00,300 --> 01:25:03,310 如果你不FCLOSE,这是一个内存泄漏。 1005 01:25:03,310 --> 01:25:06,680 我们要FCLOSE我们最初打开的文件,我认为这是它的。 1006 01:25:06,680 --> 01:25:13,810 如果我们达到这个目标,我已经有问题。 1007 01:25:13,810 --> 01:25:17,260 让我们来看看。 1008 01:25:17,260 --> 01:25:19,960 那有什么抱怨吗? 1009 01:25:19,960 --> 01:25:30,220 预计'诠释',但实参的类型结构_IO_FILE *“。 1010 01:25:36,850 --> 01:25:39,370 我们可以看到,如果这样的作品。 1011 01:25:45,210 --> 01:25:53,540 只允许在C99。 Augh。好吧,让hacker_typer。 1012 01:25:53,540 --> 01:25:57,760 现在,我们得到更多有用的描述。 1013 01:25:57,760 --> 01:25:59,900 因此,使用未声明的标识符“normal_settings”。 1014 01:25:59,900 --> 01:26:04,170 我没有把它normal_settings。我称为它current_settings。 1015 01:26:04,170 --> 01:26:12,090 因此,让我们改变这一切。 1016 01:26:17,920 --> 01:26:21,710 现在传递参数。 1017 01:26:26,290 --> 01:26:29,500 现在,我会让0。 1018 01:26:29,500 --> 01:26:36,720 好吧。 / hacker_typer cp.c. 1019 01:26:36,720 --> 01:26:39,590 我也并没有明确的开始屏幕上。 1020 01:26:39,590 --> 01:26:42,960 但是你可以回头看的最后一个问题集,就看你如何清除屏幕。 1021 01:26:42,960 --> 01:26:45,160 这就是打印一些字符 1022 01:26:45,160 --> 01:26:47,210 虽然这是做我想做的事情。 1023 01:26:47,210 --> 01:26:48,900 好吧。 1024 01:26:48,900 --> 01:26:55,280 思考为什么这个需要,而不是标准输入为0, 1025 01:26:55,280 --> 01:27:00,560 应#定义0, 1026 01:27:00,560 --> 01:27:03,890 这是抱怨 - 1027 01:27:13,150 --> 01:27:19,360 以前我说的文件描述符,但你也有你的FILE *, 1028 01:27:19,360 --> 01:27:23,210 一个文件描述符只是一个单一的整数, 1029 01:27:23,210 --> 01:27:26,970 而一个FILE *有一大堆的东西,与它相关联的。 1030 01:27:26,970 --> 01:27:30,380 究其原因,我们需要说0,而不是标准输入 1031 01:27:30,380 --> 01:27:37,480 的是,stdin是一个FILE *,它指向的东西被引用文件描述符0。 1032 01:27:37,480 --> 01:27:45,070 因此,即使在这里,当我做FOPEN(ARGV [1],我得到一个FILE *。 1033 01:27:45,070 --> 01:27:51,180 但是,在该文件中*是对应于该文件的文件描述符的事情。 1034 01:27:51,180 --> 01:27:57,430 如果你看看在开放的手册页,所以我认为你必须做的人3个开放式的 - 都能跟得上 - 1035 01:27:57,430 --> 01:27:59,380 男子2个开放 - 是啊。 1036 01:27:59,380 --> 01:28:06,250 如果你看一下在页面的开放,开放是像一个较低的水平FOPEN, 1037 01:28:06,250 --> 01:28:09,350 它返回的实际文件描述符。 1038 01:28:09,350 --> 01:28:12,050 的FOPEN做了一堆东西之上的开放, 1039 01:28:12,050 --> 01:28:17,640 而不是只是返回的文件描述符FILE *指针返回一个整 1040 01:28:17,640 --> 01:28:20,590 里面,这是我们的小文件描述符。 1041 01:28:20,590 --> 01:28:25,020 所以标准是指FILE *的事情, 1042 01:28:25,020 --> 01:28:29,120 而0指的是刚才的文件描述标准本身。 1043 01:28:29,120 --> 01:28:32,160 >> 有问题吗? 1044 01:28:32,160 --> 01:28:35,930 [笑]吹过。 1045 01:28:35,930 --> 01:28:39,140 好的。我们就大功告成了。 [笑] 1046 01:28:39,140 --> 01:28:42,000 >> [CS50.TV]