1 00:00:00,000 --> 00:00:02,730 [Powered by Google Translate] [第5条:不太舒服] 2 00:00:02,730 --> 00:00:05,180 [内特 - 哈迪森,哈佛大学] 3 00:00:05,180 --> 00:00:08,260 这是CS50。[CS50.TV] 4 00:00:08,260 --> 00:00:11,690 所以欢迎回来,伙计们。 5 00:00:11,690 --> 00:00:16,320 第5节。 6 00:00:16,320 --> 00:00:20,220 在这一点上,已完成测验,并看到了你所做的, 7 00:00:20,220 --> 00:00:25,770 希望你觉得真的很不错,因为我的分数在本节留下了非常深刻的印象。 8 00:00:25,770 --> 00:00:28,050 对于我们的在线观众,我们有几个问题 9 00:00:28,050 --> 00:00:33,680 有关问题上的最后两个问题 - 或测验,而。 10 00:00:33,680 --> 00:00:39,690 因此,我们要对这些真的很快,让大家看到发生了什么事 11 00:00:39,690 --> 00:00:45,060 以及如何通过实际的解决方案,而不是仅仅查看解决方案本身。 12 00:00:45,060 --> 00:00:50,330 我们要在过去的几个问题真的很快,32和33。 13 00:00:50,330 --> 00:00:53,240 只是,再次,使在线的观众可以看到这一点。 14 00:00:53,240 --> 00:00:59,080 >> 如果你把你的问题是32,第13页, 15 00:00:59,080 --> 00:01:02,730 13 16 32,问题是所有有关掉期。 16 00:01:02,730 --> 00:01:05,010 这是所有关于交换两个整数。 17 00:01:05,010 --> 00:01:08,740 这是问题,一对夫妇在演讲的时候,我们已经走了。 18 00:01:08,740 --> 00:01:13,590 在这里,我们要你做的是一个快速的记忆痕迹。 19 00:01:13,590 --> 00:01:17,000 要填写的值的变量,因为它们是在栈上 20 00:01:17,000 --> 00:01:20,250 的代码通过这个交换功能。 21 00:01:20,250 --> 00:01:24,500 特别是我们正在寻找的 - 我打算把这款iPad - 22 00:01:24,500 --> 00:01:29,650 特别是,我们看到的是,这条线在这里6。 23 00:01:29,650 --> 00:01:36,740 而它的编号为6的连续性与前面的问题。 24 00:01:36,740 --> 00:01:41,720 我们想要做的是显示或标签的内存状态 25 00:01:41,720 --> 00:01:46,090 因为它是在的时候,当我们执行这条线6号, 26 00:01:46,090 --> 00:01:52,540 这实际上是一个回报,我们这里的交换功能。 27 00:01:52,540 --> 00:01:59,450 如果我们向下滚动在这里,我们看到的一切都在内存中的地址提供给我们的。 28 00:01:59,450 --> 00:02:02,540 这是非常关键的,我们会回来的,在短短的时刻。 29 00:02:02,540 --> 00:02:09,240 然后在这里的底部,我们有一个小的内存图,我们要参考。 30 00:02:09,240 --> 00:02:12,490 其实我已经做到了这一点,在我的iPad。 31 00:02:12,490 --> 00:02:20,720 所以,我要交替之间来回iPad和这个代码仅作参考。 32 00:02:20,720 --> 00:02:26,540 >> 让我们开始。首先,让我们把注意力集中在这里的第一对夫妇行的主要。 33 00:02:26,540 --> 00:02:30,220 首先,我们要初始化x为1和y 2。 34 00:02:30,220 --> 00:02:33,040 因此,我们有两个整型变量,它们都是被放置在栈上。 35 00:02:33,040 --> 00:02:36,050 我们打​​算把他们的A 1和A 2。 36 00:02:36,050 --> 00:02:43,150 所以,如果我翻转过来,希望我的iPad,让我们来看看 - 37 00:02:43,150 --> 00:02:48,660 Apple TV的镜像,和我们走吧。好吧。 38 00:02:48,660 --> 00:02:51,670 所以,如果我翻转过来,以我的iPad, 39 00:02:51,670 --> 00:02:56,220 我想初始化x为1和y 2。 40 00:02:56,220 --> 00:03:00,580 我们做到这一点很简单,写一个包装盒中标记为x 1 41 00:03:00,580 --> 00:03:07,730 2包装盒中标记为Y。相当简单的。 42 00:03:07,730 --> 00:03:11,620 所以,现在让我们回到笔记本电脑,看看接下来会发生什么。 43 00:03:11,620 --> 00:03:15,810 因此,这下一行是在事情变得棘手。 44 00:03:15,810 --> 00:03:28,110 我们传递的地址的x和y的地址作为参数a和b的交换函数。 45 00:03:28,110 --> 00:03:32,380 x和y的地址,该地址是我们无法计算的东西, 46 00:03:32,380 --> 00:03:36,360 没有这些子弹点就在这里。 47 00:03:36,360 --> 00:03:39,750 幸运的是,前两个要点告诉我们的答案是什么。 48 00:03:39,750 --> 00:03:44,740 x的地址是10,在存储器中,存储器中的y的地址是14。 49 00:03:44,740 --> 00:03:51,870 因此,那些得到的值通过在A和B的向上顶在我们的交换功能。 50 00:03:51,870 --> 00:04:00,760 所以,再一次,切换回我们的图,我可以写在10 51 00:04:00,760 --> 00:04:07,400 和14 b中。 52 00:04:07,400 --> 00:04:11,610 现在,这一点是我们进行交换。 53 00:04:11,610 --> 00:04:14,520 所以再次翻转的笔记本电脑, 54 00:04:14,520 --> 00:04:21,079 我们可以看到,交换的工作方式是我第一次解引用一个,并将结果存储在tmp。 55 00:04:21,079 --> 00:04:27,650 因此,解引用运算符说,“嘿,治疗的变量a的地址的内容。 56 00:04:27,650 --> 00:04:33,830 无论是存储在该地址,并加载它。“ 57 00:04:33,830 --> 00:04:41,720 你加载的变量将被存储到我们的TMP变量。 58 00:04:41,720 --> 00:04:45,150 翻转到iPad。 59 00:04:45,150 --> 00:04:51,690 如果我们去解决10,我们知道地址10是输出变x 60 00:04:51,690 --> 00:04:55,480 告诉我们,因为我们的子弹点x在内存中的地址是10。 61 00:04:55,480 --> 00:05:00,180 因此,我们可以去那里,得到了它的价值,这是1,因为我们看到我们的iPad, 62 00:05:00,180 --> 00:05:06,300 并加载到tmp目录。 63 00:05:06,300 --> 00:05:08,250 再次,这是不是最后的内容。 64 00:05:08,250 --> 00:05:14,350 我们要走过和结束时,我们会得到我们的最终状态的程序。 65 00:05:14,350 --> 00:05:17,210 但是现在,我们的值为1,存储在tmp。 66 00:05:17,210 --> 00:05:19,210 >> 在这里有一个快速的问题。 67 00:05:19,210 --> 00:05:23,980 [亚历山大]是解引用运算符 - 这只是前面的变量的明星权吗? 68 00:05:23,980 --> 00:05:27,600 “是的。因此,解引用运算符,当我们打开再次回到我们的笔记本电脑, 69 00:05:27,600 --> 00:05:33,780 这是明星就在眼前。 70 00:05:33,780 --> 00:05:37,460 从这个意义上说,它是 - 你对比乘法运算符 71 00:05:37,460 --> 00:05:42,400 这需要两件事情:解引用运算符是一元运算符。 72 00:05:42,400 --> 00:05:46,130 刚刚申请到一个值,而不是一个二元操作符, 73 00:05:46,130 --> 00:05:48,810 适用于两种不同的价值观。 74 00:05:48,810 --> 00:05:52,080 所以,这就是发生在这条线。 75 00:05:52,080 --> 00:05:58,390 我们装的值为1,并把它保存到我们的临时整型变量。 76 00:05:58,390 --> 00:06:05,800 下一行,我们的内容存储的B - 77 00:06:05,800 --> 00:06:12,630 或者说,我们,b是指向到的地方,一个是指向存储的内容。 78 00:06:12,630 --> 00:06:17,690 如果我们分析这个由右至左,我们将取消引用b, 79 00:06:17,690 --> 00:06:23,580 我们要解决14,我们要抢的整数,它是有, 80 00:06:23,580 --> 00:06:26,900 然后我们要去的地址10, 81 00:06:26,900 --> 00:06:34,240 我们要抛的结果,我们解引用的b到该空间。 82 00:06:34,240 --> 00:06:40,080 翻转回我们的iPad,在这里我们可以使这一点更具体的, 83 00:06:40,080 --> 00:06:44,070 它可能会帮助,如果我在这里写号上的所有地址。 84 00:06:44,070 --> 00:06:53,820 因此,我们知道,我们在y地址为14,x是地址10。 85 00:06:53,820 --> 00:07:00,180 当我们开始在B,我们解引用b,我们要抓住的价值2。 86 00:07:00,180 --> 00:07:08,320 我们要抓住这个值,因为是住在地址14的值。 87 00:07:08,320 --> 00:07:15,700 我们打​​算把它的变量,居住地址10, 88 00:07:15,700 --> 00:07:19,160 这是正确的,相应的变量x。 89 00:07:19,160 --> 00:07:21,810 所以,我们可以做一点点的覆盖这里 90 00:07:21,810 --> 00:07:35,380 我们摆脱我们的1,而我们写了一个2。 91 00:07:35,380 --> 00:07:39,560 因此,所有的好和良好的世界,即使我们已经覆盖x现在。 92 00:07:39,560 --> 00:07:44,890 我们已经存储在我们的TMP变量x的旧值。 93 00:07:44,890 --> 00:07:50,210 因此,我们可以完成交换的下一行。 94 00:07:50,210 --> 00:07:53,030 翻转回我们的笔记本电脑。 95 00:07:53,030 --> 00:07:58,150 现在,剩下的就是我们的临时整型变量的内容 96 00:07:58,150 --> 00:08:05,630 并将它们存储到地址b被保持住在变量。 97 00:08:05,630 --> 00:08:10,230 因此,我们要有效地解引用b的变量 98 00:08:10,230 --> 00:08:14,340 这是在它的地址是b持有, 99 00:08:14,340 --> 00:08:19,190 我们将tmp是保存它的东西的价值。 100 00:08:19,190 --> 00:08:23,280 翻转到iPad一次。 101 00:08:23,280 --> 00:08:31,290 我在这里可以删除此值,2, 102 00:08:31,290 --> 00:08:41,010 相反,我们将复制到它的。 103 00:08:41,010 --> 00:08:43,059 然后,下一行执行的,当然 - 104 00:08:43,059 --> 00:08:47,150 如果我们翻回的笔记本电脑 - 这是6点, 105 00:08:47,150 --> 00:08:52,500 这是我们希望我们的图完全充满。 106 00:08:52,500 --> 00:08:58,940 所以,翻转到iPad,只是让你可以看到已完成的图, 107 00:08:58,940 --> 00:09:06,610 你可以看到,我们在10,14在b,1 tmp中,2 x中,在y和1。 108 00:09:06,610 --> 00:09:11,000 是否有任何问题吗? 109 00:09:11,000 --> 00:09:14,640 这是否更有意义,已经走过吗? 110 00:09:14,640 --> 00:09:24,850 就不是那么有意义吗?但愿不会。好吧。 111 00:09:24,850 --> 00:09:28,230 >> 指针是一个非常棘手的问题。 112 00:09:28,230 --> 00:09:33,420 与我们合作的球员之一有一个很常见的说法: 113 00:09:33,420 --> 00:09:36,590 “要了解指针,你必须先了解指针。” 114 00:09:36,590 --> 00:09:40,530 我认为这是非常真实的。它确实需要一段时间才能使用它。 115 00:09:40,530 --> 00:09:45,360 抽签的图片,像这样的抽签的内存图是非常有帮助的, 116 00:09:45,360 --> 00:09:49,480 后例如,在你走过的例子后,例如, 117 00:09:49,480 --> 00:09:54,450 它会开始做一些更有意义一些更有意义和更有意义一点。 118 00:09:54,450 --> 00:10:01,560 终于,有一天,你会拥有这一切完全掌握。 119 00:10:01,560 --> 00:10:13,800 任何问题之前,我们进入下一个问题吗?好的。 120 00:10:13,800 --> 00:10:18,840 因此,翻转的笔记本电脑。 121 00:10:18,840 --> 00:10:23,300 我们的下一个问题是问题33号文件I / O。 122 00:10:23,300 --> 00:10:26,350 这是一个有点放大。 123 00:10:26,350 --> 00:10:28,710 问题33 - 是吗? 124 00:10:28,710 --> 00:10:32,110 >> [丹尼尔]我刚做了一个简单的问题。此星或星号, 125 00:10:32,110 --> 00:10:35,590 这就是所谓的提领时,你使用一个星号之​​前。 126 00:10:35,590 --> 00:10:38,820 它是什么时调用之前使用的符号吗? 127 00:10:38,820 --> 00:10:43,140 >>“符号前是运营商的地址。 128 00:10:43,140 --> 00:10:45,880 因此,让我们向上卷动。 129 00:10:45,880 --> 00:10:49,310 哎呀。我在变焦模式,所以我真的不能滚动。 130 00:10:49,310 --> 00:10:52,780 如果我们看一下这段代码,在这里真的很快, 131 00:10:52,780 --> 00:10:54,980 再次,同样的事情发生。 132 00:10:54,980 --> 00:10:59,180 如果我们看一下这段代码,在这里,在这条线,我们拨打电话交换, 133 00:10:59,180 --> 00:11:10,460 “&”只是说“的地址在变量x的生活。” 134 00:11:10,460 --> 00:11:14,460 当你的编译器编译你的代码, 135 00:11:14,460 --> 00:11:20,590 它实际物理标记在内存中所有的变量住的地方。 136 00:11:20,590 --> 00:11:24,910 所以编译器就可以做一次,它的编译一切, 137 00:11:24,910 --> 00:11:31,110 都知道,“哦,我把x地址10地址14,我要把Y”。 138 00:11:31,110 --> 00:11:34,640 然后,它可以为你填写这些值。 139 00:11:34,640 --> 00:11:44,740 这样你就可以 - 它可以通过本和通&Y以及。 140 00:11:44,740 --> 00:11:50,730 这些人得到的地址,但他们也,当你通过它们的交换功能, 141 00:11:50,730 --> 00:11:55,690 这此int *类型的信息,在这里,告诉编译器, 142 00:11:55,690 --> 00:12:01,350 “好了,我们将要解释这个地址作为一个整型变量的地址。” 143 00:12:01,350 --> 00:12:05,900 一个int作为一个地址,它是从一个字符变量的地址不同 144 00:12:05,900 --> 00:12:09,930 因为一个int占用了一个32位的机器上,占用4个字节的空间, 145 00:12:09,930 --> 00:12:13,310 而一个字符只占用1个字节的空间。 146 00:12:13,310 --> 00:12:17,310 因此,它是重要的是要知道什么是 - 生活中,什么类型的值 147 00:12:17,310 --> 00:12:20,340 住在传入的地址,进行了 148 00:12:20,340 --> 00:12:22,020 或地址,你的工作。 149 00:12:22,020 --> 00:12:29,020 这样一来,你知道多少个字节的信息的实际加载的RAM。 150 00:12:29,020 --> 00:12:31,780 然后,是的,这种反引用运算符,就像你问, 151 00:12:31,780 --> 00:12:37,200 在一个特定的地址去访问信息。 152 00:12:37,200 --> 00:12:42,820 所以说,这是一个变量,在这里,治疗的内容作为地址, 153 00:12:42,820 --> 00:12:47,880 去到该地址,并拉出,到寄存器加载到处理器中,负载 154 00:12:47,880 --> 00:12:56,340 实际值或住在该地址的内容。 155 00:12:56,340 --> 00:12:59,620 还有什么问题吗?这些都是很好的问​​题。 156 00:12:59,620 --> 00:13:01,650 这是一个很多新的术语。 157 00:13:01,650 --> 00:13:09,800 这也是一种时髦,看到&和*在不同的地方。 158 00:13:09,800 --> 00:13:13,180 >> 好的。 159 00:13:13,180 --> 00:13:18,530 所以回到问题33,文件I / O。 160 00:13:18,530 --> 00:13:22,540 这是一个发生的几件事情,我认为这些问题。 161 00:13:22,540 --> 00:13:25,400 一,这是一个相当新的课题。 162 00:13:25,400 --> 00:13:30,590 据介绍很快测验前, 163 00:13:30,590 --> 00:13:33,400 然后,我认为这是种像这些字中的数学问题之一 164 00:13:33,400 --> 00:13:39,720 ,他们给你很多的信息,但实际上你不使用一吨的。 165 00:13:39,720 --> 00:13:44,060 这个问题的第一部分描述的是一个CSV文件是什么。 166 00:13:44,060 --> 00:13:50,620 现在,一个CSV文件,根据描述,是一个以逗号分隔值文件。 167 00:13:50,620 --> 00:13:55,300 因此,这些在所有有趣的是,和你使用过他们的原因, 168 00:13:55,300 --> 00:14:00,800 ,因为,你们有多少人曾经使用Excel之类的东西吗? 169 00:14:00,800 --> 00:14:03,240 图你最有可能,或将使用在一些点在你的生活中。 170 00:14:03,240 --> 00:14:06,430 你会使用Excel之类的东西。 171 00:14:06,430 --> 00:14:10,940 为了得到数据的Excel电子表格或做任何形式的处理, 172 00:14:10,940 --> 00:14:17,240 如果你想编写一个C程序或Python程序,Java程序, 173 00:14:17,240 --> 00:14:20,070 处理的数据储存在那里, 174 00:14:20,070 --> 00:14:23,170 把它弄出来的最常见的方式之一,是一个CSV文件中。 175 00:14:23,170 --> 00:14:26,850 ,你可以打开Excel中,当你去到“另存为”对话, 176 00:14:26,850 --> 00:14:32,840 你可以得到一个实际的CSV文件。 177 00:14:32,840 --> 00:14:35,890 >> 很方便的知道如何处理这些事情。 178 00:14:35,890 --> 00:14:42,010 它的工作方式是,它是类似的 - 我的意思是,它本质上是模仿电子表格, 179 00:14:42,010 --> 00:14:47,590 在那里,我们在这里看到,最左边的一块, 180 00:14:47,590 --> 00:14:49,910 我们所有的姓氏。 181 00:14:49,910 --> 00:14:54,670 因此,我们有马兰,那么哈迪森,然后鲍登,MacWilliam,然后生。 182 00:14:54,670 --> 00:14:59,470 所有的姓氏。然后一个逗号分隔的姓氏第一个名字。 183 00:14:59,470 --> 00:15:02,970 大卫奈特,抢劫,张宇,和Zamyla。 184 00:15:02,970 --> 00:15:06,850 我总是混淆:罗比和汤姆。 185 00:15:06,850 --> 00:15:10,940 然后,终于,第三列是电子邮件地址。 186 00:15:10,940 --> 00:15:18,500 一旦你了解了,其余的程序是非常简单的实现。 187 00:15:18,500 --> 00:15:23,850 我们所做的,以模仿相同的结构在我们的C程序 188 00:15:23,850 --> 00:15:27,510 是我们使用了一个结构。 189 00:15:27,510 --> 00:15:30,520 我们将开始播放这一点以及。 190 00:15:30,520 --> 00:15:35,790 我们看到了他们的第一个问题集3,当我们正在处理的字典点点。 191 00:15:35,790 --> 00:15:40,290 但是,这名员工结构存储一个姓氏,一个名字,和电子邮件。 192 00:15:40,290 --> 00:15:44,500 就像我们的CSV文件存储。 193 00:15:44,500 --> 00:15:47,950 因此,这只是从一种格式转换到另一个。 194 00:15:47,950 --> 00:15:54,630 我们必须转换,在这种情况下,工作人员成一条线的结构的, 195 00:15:54,630 --> 00:15:59,060 一个以逗号分隔的行,就这样。 196 00:15:59,060 --> 00:16:01,500 这是否有意义吗?你们都采取了有奖问答, 197 00:16:01,500 --> 00:16:07,680 所以我想你已经至少有一些时间来思考这个问题。 198 00:16:07,680 --> 00:16:16,410 >> 在租用功能,这道题我们 - 我们将在这一点位变焦 - 199 00:16:16,410 --> 00:16:22,480 在人员结构,人员结构,名称为s, 200 00:16:22,480 --> 00:16:30,900 并附加其内容到我们的staff.csv文件。 201 00:16:30,900 --> 00:16:34,230 事实证明,这是非常简单的使用。 202 00:16:34,230 --> 00:16:37,430 我们将发挥着这些功能,今天多一点点。 203 00:16:37,430 --> 00:16:44,510 但是,在这种情况下,fprintf函数是真正的关键。 204 00:16:44,510 --> 00:16:51,960 因此,fprintf,我们可以打印,就像你们一直在使用printf的整个任期。 205 00:16:51,960 --> 00:16:55,050 您可以输出到一个文件中的行。 206 00:16:55,050 --> 00:16:59,030 因此,而不是只是通常的printf调用,你给它一个格式字符串 207 00:16:59,030 --> 00:17:05,380 然后你替换所有的变量与下面的参数, 208 00:17:05,380 --> 00:17:11,290 与fprintf,你的第一个参数,而不是你要写入的文件。 209 00:17:11,290 --> 00:17:21,170 如果我们要看看这个设备,例如,人fprintf, 210 00:17:21,170 --> 00:17:25,980 我们可以看到的printf和fprintf的区别。 211 00:17:25,980 --> 00:17:28,960 我将在这里一点点放大。 212 00:17:28,960 --> 00:17:33,140 因此,用printf,我们给它一个格式字符串,然后在随后的参数 213 00:17:33,140 --> 00:17:37,580 更换或替换成我们的格式化字符串中的所有的变量。 214 00:17:37,580 --> 00:17:47,310 而与fprintf,第一个参数是事实上,这称为流文件*。 215 00:17:47,310 --> 00:17:51,800 >> 在这里,我们的租赁, 216 00:17:51,800 --> 00:17:54,550 我们已经为我们打开了我们的文件*流。 217 00:17:54,550 --> 00:17:57,810 那这是什么第一线;它打开了staff.csv的文件, 218 00:17:57,810 --> 00:18:01,690 它以附加的方式打开它,并为我们做了所有剩下的是 219 00:18:01,690 --> 00:18:08,640 编写人员结构的文件。 220 00:18:08,640 --> 00:18:10,870 而且,让我们来看看,我想使用iPad? 221 00:18:10,870 --> 00:18:17,900 我会使用iPad。我们有无效的 - 让我们把这个放在桌子上,这样我就可以写一个好一点的 - 222 00:18:17,900 --> 00:18:33,680 无效租用,它需要一个参数,称为s的人员结构。 223 00:18:33,680 --> 00:18:44,120 得到了我们的大括号,我们有我们的文件*文件, 224 00:18:44,120 --> 00:18:48,380 我们有我们的FOPEN线给我们, 225 00:18:48,380 --> 00:18:51,890 ,我就写点,因为它已经在百科。 226 00:18:51,890 --> 00:19:00,530 然后在我们的下一行,我们要拨打电话为fprintf 227 00:19:00,530 --> 00:19:03,700 和我们将要通过的文件中,我们要打印的, 228 00:19:03,700 --> 00:19:10,290 然后我们的格式化字符串,其中 - 229 00:19:10,290 --> 00:19:14,300 我会告诉你们告诉我它是什么样子。 230 00:19:14,300 --> 00:19:20,500 你怎么样,Stella吗?你知道的格式字符串的第一部分看起来像什么? 231 00:19:20,500 --> 00:19:24,270 斯特拉]我不知道。 >>随意问吉米。 232 00:19:24,270 --> 00:19:27,690 你知道吗,吉米? 233 00:19:27,690 --> 00:19:31,000 [麦]这是最后一个吗?我不知道。我不能完全肯定。 234 00:19:31,000 --> 00:19:39,020 “好了。怎么样,有没有人得到这个正确的成绩? 235 00:19:39,020 --> 00:19:41,770 所有权利。 236 00:19:41,770 --> 00:19:47,920 原来,这里所有我们需要做的是,我们希望我们的人员结构各部分的 237 00:19:47,920 --> 00:19:53,290 作为一个字符串到我们的文件被打印出来。 238 00:19:53,290 --> 00:19:59,900 我们只是使用字符串替换字符,三个不同的时间,因为我们有个姓 239 00:19:59,900 --> 00:20:07,160 其次是逗号,然后一个名字后跟一个逗号, 240 00:20:07,160 --> 00:20:12,430 然后最后的电子邮件地址,其次是 - 这是不 241 00:20:12,430 --> 00:20:15,140 适合我的屏幕上 - 但它后面的一个换行符。 242 00:20:15,140 --> 00:20:20,060 所以我要去写它只是出现了下滑。 243 00:20:20,060 --> 00:20:23,560 然后按照我们的格式化字符串, 244 00:20:23,560 --> 00:20:27,880 我们只需要换人,我们使用点表示法访问 245 00:20:27,880 --> 00:20:31,370 我们所看到的问题集3。 246 00:20:31,370 --> 00:20:48,820 我们可以使用s.last,s.first,s.email 247 00:20:48,820 --> 00:20:58,990 在这三个值到我们的格式化字符串来代替。 248 00:20:58,990 --> 00:21:06,190 所以,这是怎么去的?有意义吗? 249 00:21:06,190 --> 00:21:09,700 是吗?不是吗?有可能吗?好吧。 250 00:21:09,700 --> 00:21:14,180 >> 最后一点,我们做印刷后,我们已经打开的文件后,我们已经: 251 00:21:14,180 --> 00:21:17,370 每当我们打开一个文件,我们总是要记住关闭。 252 00:21:17,370 --> 00:21:19,430 否则,我们将最终的内存泄漏, 253 00:21:19,430 --> 00:21:22,500 使用文件描述符。 254 00:21:22,500 --> 00:21:25,950 因此,要关闭它,我们使用哪些功能?丹尼尔? 255 00:21:25,950 --> 00:21:30,120 [丹尼尔] FCLOSE? >> FCLOSE,准确。 256 00:21:30,120 --> 00:21:37,520 因此,这个问题的最后一部分是正确地关闭文件,使用fclose函数, 257 00:21:37,520 --> 00:21:40,370 它只是看起来像。 258 00:21:40,370 --> 00:21:43,880 太疯狂了。 259 00:21:43,880 --> 00:21:46,990 酷。 260 00:21:46,990 --> 00:21:49,520 所以这就是问题的测验33。 261 00:21:49,520 --> 00:21:52,480 I / O来了,我们肯定将有更多的文件。 262 00:21:52,480 --> 00:21:55,130 我们会做多一点点在今天的演讲,或在今天的部分, 263 00:21:55,130 --> 00:22:01,710 因为这是怎么回事形成这个即将到来的pset中的大部分。 264 00:22:01,710 --> 00:22:05,020 让我们继续在这一点上测验。是吗? 265 00:22:05,020 --> 00:22:10,880 >> [夏洛特]为什么FCLOSE(文件),而不是FCLOSE(staff.csv)? 266 00:22:10,880 --> 00:22:19,100 “啊。因为事实证明 - 这样的问题,这是一个伟大的, 267 00:22:19,100 --> 00:22:27,800 正因为如此,当我们写FCLOSE,我们,写FCLOSE(文件)明星变量 268 00:22:27,800 --> 00:22:33,680 而不是在文件名的,staff.csv?是正确的吗?是啊。 269 00:22:33,680 --> 00:22:39,570 因此,让我们一起来看看。如果我切换回我的笔记本电脑, 270 00:22:39,570 --> 00:22:45,040 让我们来看看fclose函数。 271 00:22:45,040 --> 00:22:51,460 因此,fclose函数关闭一个流,它需要的指针,我们要关闭的流, 272 00:22:51,460 --> 00:22:57,010 而不是实际文件名,我们要关闭。 273 00:22:57,010 --> 00:23:01,620 这是因为在幕后,当你拨打电话的FOPEN, 274 00:23:01,620 --> 00:23:12,020 当你打开一个文件时,你实际上是在分配内存来存储信息的文件。 275 00:23:12,020 --> 00:23:16,380 所以,你必须有信息的文件的文件指针, 276 00:23:16,380 --> 00:23:23,080 如它是开放的,它的大小,在那里你目前在该文件中, 277 00:23:23,080 --> 00:23:29,100 这样就可以在文件中读取和写入到特定的地方调用。 278 00:23:29,100 --> 00:23:38,060 您最终成交的指针,而不是关闭的文件名。 279 00:23:38,060 --> 00:23:48,990 >> 是吗? [丹尼尔]因此,为了使用租用,你会说 - 它是如何获得用户输入? 280 00:23:48,990 --> 00:23:53,830 fprintf像在这个意义上,它会等待用户输入的GetString 281 00:23:53,830 --> 00:23:57,180 要求用户输入 - 等待你输入这三样东西? 282 00:23:57,180 --> 00:24:00,480 或者你需要使用的东西,落实租? 283 00:24:00,480 --> 00:24:04,100 >>呀。因此,我们不 - 的问题是,如何得到用户输入 284 00:24:04,100 --> 00:24:09,220 为了实现租吗?我们这里是出租的呼叫者, 285 00:24:09,220 --> 00:24:17,690 在这种人员结构,所有的数据存储结构已经通过。 286 00:24:17,690 --> 00:24:22,990 因此,fprintf是只是数据直接写入到文件。 287 00:24:22,990 --> 00:24:25,690 有没有等待用户输入。 288 00:24:25,690 --> 00:24:32,110 用户是否已经输入适当的把它在这名员工结构。 289 00:24:32,110 --> 00:24:36,510 的事情,当然,将打破,如果这些指针为空的, 290 00:24:36,510 --> 00:24:40,370 因此,我们在这里向后滚动,我们期待在我们的结构。 291 00:24:40,370 --> 00:24:43,640 我们有字符串,字符串的第一个字符串的邮件。 292 00:24:43,640 --> 00:24:48,530 现在我们知道,所有的那些真正的,引擎盖下,是char *变量。 293 00:24:48,530 --> 00:24:53,470 这可能会或可能不会被指向空。 294 00:24:53,470 --> 00:24:55,800 他们可能指向堆内存, 295 00:24:55,800 --> 00:24:59,650 也许内存堆栈。 296 00:24:59,650 --> 00:25:04,580 我们真的不知道,但如果其中任一指针是空的,或无效的, 297 00:25:04,580 --> 00:25:08,120 一定会崩溃,我们的租赁功能。 298 00:25:08,120 --> 00:25:11,050 这是什么,这是一种超越的考试范围。 299 00:25:11,050 --> 00:25:16,440 我们不担心这一点。 300 00:25:16,440 --> 00:25:22,170 大。好吧。因此移动从测验。 301 00:25:22,170 --> 00:25:25,760 >> 让我们来关闭这个家伙,我们要看看在pset中4。 302 00:25:25,760 --> 00:25:34,700 所以,如果你们看在pset规范,一旦你可以访问它的,cs50.net/quizzes, 303 00:25:34,700 --> 00:25:42,730 我们要经过几个部分问题,今天。 304 00:25:42,730 --> 00:25:52,240 我向下滚动 - 部分问题开始的第三页上的pset的规范。 305 00:25:52,240 --> 00:25:57,800 第一部分要求你去观看短重定向和管道。 306 00:25:57,800 --> 00:26:02,820 这是一个很酷的短,显示出一些新的,很酷的命令行技巧,您可以使用。 307 00:26:02,820 --> 00:26:06,050 然后,我们有几个问题要问你。 308 00:26:06,050 --> 00:26:10,860 这第一个问题,关于流,输出写入默认情况下, 309 00:26:10,860 --> 00:26:15,920 一种感动刚才只是一点点。 310 00:26:15,920 --> 00:26:22,380 我们刚才谈到的这fprintf需要在一个文件中*流作为它的参数。 311 00:26:22,380 --> 00:26:26,580 FCLOSE需要在一个文件中*流为好, 312 00:26:26,580 --> 00:26:32,660 和返回值的FOPEN给你一个文件*流以及。 313 00:26:32,660 --> 00:26:36,060 究其原因,我们还没有看到那些之前我们已经处理了printf 314 00:26:36,060 --> 00:26:39,450 是因为输出有一个默认的流。 315 00:26:39,450 --> 00:26:41,810 而它的默认流写 316 00:26:41,810 --> 00:26:45,190 你会发现在短期内。 317 00:26:45,190 --> 00:26:50,080 所以,一定要看看它。 318 00:26:50,080 --> 00:26:53,010 >> 在今天的部分,我们要谈一点关于GDB 319 00:26:53,010 --> 00:26:57,720 ,因为你是比较熟悉的,它更多的练习,你得到它, 320 00:26:57,720 --> 00:27:01,390 你能够更好地将追捕在自己的代码错误。 321 00:27:01,390 --> 00:27:05,540 这加快了极大的调试过程。 322 00:27:05,540 --> 00:27:09,230 因此,通过使用printf,每次你这样做,你必须重新编译你的代码, 323 00:27:09,230 --> 00:27:13,000 你必须再次运行它,有时你必须左右移动printf调用, 324 00:27:13,000 --> 00:27:17,100 注释掉的代码,它只是需要一段时间。 325 00:27:17,100 --> 00:27:20,850 我们的目标是尝试说服你,你基本上可以用GDB, 326 00:27:20,850 --> 00:27:26,810 在任何时候在你的代码中的printf什么,你永远不会有重新编译它。 327 00:27:26,810 --> 00:27:35,120 你从来没有开始,并不断猜测的printf下。 328 00:27:35,120 --> 00:27:40,910 要做的第一件事是复制这条线和部分代码的网站。 329 00:27:40,910 --> 00:27:47,530 我说,“wget的http://cdn.cs50.net”这行代码复制。 330 00:27:47,530 --> 00:27:49,510 我要复制它。 331 00:27:49,510 --> 00:27:55,950 我要到我的设备,缩放,所以你可以看到我在做什么, 332 00:27:55,950 --> 00:28:01,890 将其粘贴在那里,当我按下Enter键,这个wget命令从字面上是一个网络获得。 333 00:28:01,890 --> 00:28:06,210 这是怎么回事拉下这个文件在互联网上传播, 334 00:28:06,210 --> 00:28:11,790 它的将它保存到当前目录中。 335 00:28:11,790 --> 00:28:21,630 现在,如果我列出当前目录中,你可以看到,我已经了这section5.zip的文件右键在那里。 336 00:28:21,630 --> 00:28:25,260 那家伙处理的方式是将其解压缩, 337 00:28:25,260 --> 00:28:27,650 您可以在命令行中,就是这样。 338 00:28:27,650 --> 00:28:31,880 Section5.zip。 339 00:28:31,880 --> 00:28:36,980 将它解压缩,创建文件夹对我来说, 340 00:28:36,980 --> 00:28:40,410 膨胀中的所有内容,让他们在那里。 341 00:28:40,410 --> 00:28:47,410 所以,现在我可以进入我的第5节目录使用cd命令。 342 00:28:47,410 --> 00:28:58,310 清除屏幕采用了明确。因此,清除屏幕。 343 00:28:58,310 --> 00:29:02,280 现在我有一个干净漂亮的终端处理。 344 00:29:02,280 --> 00:29:06,200 >> 现在,如果我列出我看到在这个目录中的所有文件, 345 00:29:06,200 --> 00:29:12,270 你看到我已经得到四个文件:buggy1,buggy2,buggy3,并buggy4,。 346 00:29:12,270 --> 00:29:16,180 我也得到了相应的文件。 347 00:29:16,180 --> 00:29:20,400 我们不会看的。c文件。 348 00:29:20,400 --> 00:29:24,140 相反,我们要使用它们时,我们打开了GDB。 349 00:29:24,140 --> 00:29:28,220 我们已经把他们周围,使我们有机会获得实际的源代码,当我们使用GDB, 350 00:29:28,220 --> 00:29:32,740 这部分的组成部分,但我们的目标是修改周围的GDB 351 00:29:32,740 --> 00:29:40,370 看看我们如何能够用它来找出什么地方出了错这四个错误的程序。 352 00:29:40,370 --> 00:29:43,380 所以我们仅仅是在房间里真的很快, 353 00:29:43,380 --> 00:29:47,000 我要问别人的bug的程序运行一个, 354 00:29:47,000 --> 00:29:54,730 ,然后我们会去通过GDB作为一个群体,我们将看看我们能做些什么来解决这些计划, 355 00:29:54,730 --> 00:29:58,460 或者至少确定发生了什么事情错在他们每个人。 356 00:29:58,460 --> 00:30:04,760 让我们从这里开始与丹尼尔。你会运行buggy1?让我们看看会发生什么。 357 00:30:04,760 --> 00:30:09,470 丹尼尔说,有一个应用程序错误。 >>呀。没错。 358 00:30:09,470 --> 00:30:12,460 所以,如果我运行buggy1,我得到了赛格故障。 359 00:30:12,460 --> 00:30:16,210 在这一点上,我可以去开拓buggy1.c, 360 00:30:16,210 --> 00:30:19,450 试图找出什么地方出了错, 361 00:30:19,450 --> 00:30:22,000 但该段故障错误最厌恶的事情之一 362 00:30:22,000 --> 00:30:27,610 的是,它不会告诉你哪一行的程序的东西实际是错误的,并打破。 363 00:30:27,610 --> 00:30:29,880 你有看的代码 364 00:30:29,880 --> 00:30:33,990 找出猜测检查或printf来看看会发生什么错误。 365 00:30:33,990 --> 00:30:37,840 关于GDB的最酷的事情之一是,它是真的,真的很容易 366 00:30:37,840 --> 00:30:42,170 要弄清楚的线在你的程序崩溃。 367 00:30:42,170 --> 00:30:46,160 这是完全值得的,使用它,即使只是。 368 00:30:46,160 --> 00:30:56,190 启动GDB,我输入GDB,然后我给它的,我想运行的可执行文件的路径。 369 00:30:56,190 --> 00:31:01,960 在这里,我打字:GDB ./buggy1。 370 00:31:01,960 --> 00:31:06,600 按下回车键。给了我这一切版权信息, 371 00:31:06,600 --> 00:31:13,000 在这里你会看到这条线,说:“从/ home /读取符号 372 00:31:13,000 --> 00:31:17,680 jharvard/section5/buggy1。“ 373 00:31:17,680 --> 00:31:22,060 如果一切顺利的话,你会看到它看起来像这样打印出的信息。 374 00:31:22,060 --> 00:31:25,500 它会读取符号,它会说:“我从你的可执行文件,读取符号” 375 00:31:25,500 --> 00:31:29,900 ,然后将这个“做”的消息在这里。 376 00:31:29,900 --> 00:31:35,410 如果你看到一些其他的变化,你看到它找不到符号 377 00:31:35,410 --> 00:31:41,460 或类似的东西,这是什么意思,你只需要编译你的可执行文件。 378 00:31:41,460 --> 00:31:49,980 当我们使用GDB编译程序,我们必须使用特殊的-g标志, 379 00:31:49,980 --> 00:31:54,540 ,这就是默认情况下,如果你编译你的程序,通过键入make 380 00:31:54,540 --> 00:31:59,320 或车或恢复,任何人。 381 00:31:59,320 --> 00:32:07,800 但是,如果你正在编译手动铛,那么你就必须去,其中包括,使用-g标志。 382 00:32:07,800 --> 00:32:10,310 >> 在这一点上,现在,我们有我们的GDB提示, 383 00:32:10,310 --> 00:32:12,310 这是很简单的运行程序。 384 00:32:12,310 --> 00:32:19,740 我们可以键入运行,我们就可以输入r。 385 00:32:19,740 --> 00:32:22,820 大多数GDB命令都可以缩写。 386 00:32:22,820 --> 00:32:25,940 通常只是一个或几个字母,这是相当不错的。 387 00:32:25,940 --> 00:32:30,980 因此,萨阿德,如果你输入r,并按下Enter键,会发生什么呢? 388 00:32:30,980 --> 00:32:39,390 萨阿德我SIGSEGV,分割故障,那么这一切的官样文章。 389 00:32:39,390 --> 00:32:43,650 >>呀。 390 00:32:43,650 --> 00:32:47,990 就像我们在屏幕上看到,现在,像萨阿德说, 391 00:32:47,990 --> 00:32:53,430 当我们键入run或r并按下回车键,我们仍然得到同样的赛格故障。 392 00:32:53,430 --> 00:32:55,830 因此,使用GDB不解决我们的问题。 393 00:32:55,830 --> 00:32:59,120 但它为我们提供了一些官样文章,而事实证明,这种官样文章 394 00:32:59,120 --> 00:33:03,080 实际上告诉了我们它的发生。 395 00:33:03,080 --> 00:33:10,680 要解析这一点,第一位的功能是,在这一切是怎么回事了。 396 00:33:10,680 --> 00:33:20,270 有__ strcmp_sse4_2,它告诉我们发生了什么事在这个文件中的 397 00:33:20,270 --> 00:33:29,450 被称为sysdeps/i386,这一切,再次,种混乱 - 而行254。 398 00:33:29,450 --> 00:33:31,670 这是一种很难解析。通常,当你看到这样的东西, 399 00:33:31,670 --> 00:33:38,770 这意味着它在一个系统库段断层。 400 00:33:38,770 --> 00:33:43,220 因此,做的strcmp。你们已经看到的strcmp。 401 00:33:43,220 --> 00:33:52,730 不是太疯狂了,但是这是否意味着STRCMP破损或STRCMP,有一个问题吗? 402 00:33:52,730 --> 00:33:57,110 你怎么想,亚历山大? 403 00:33:57,110 --> 00:34:04,890 [亚历山大]是 - 是254的行吗? - 不是二进制的,但是这不是他们的天花板, 404 00:34:04,890 --> 00:34:10,590 然后有每个函数的另一种语言。 254在该函数中,或者 - ? 405 00:34:10,590 --> 00:34:21,460 >>这是第254行。它看起来像。文件,所以它的汇编代码可能。 406 00:34:21,460 --> 00:34:25,949 >> 但是,我想更紧迫的事情,因为我们已经得到了赛格故障, 407 00:34:25,949 --> 00:34:29,960 它看起来像它来自strcmp函数, 408 00:34:29,960 --> 00:34:38,030 ,那么,这是否意味着strcmp的坏了? 409 00:34:38,030 --> 00:34:42,290 它不应该有希望。因此,仅仅因为你有一个分割故障 410 00:34:42,290 --> 00:34:49,480 系统的功能之一,通常这意味着,你只是还没有被称为是正确的。 411 00:34:49,480 --> 00:34:52,440 的事情弄清楚什么是怎么回事 412 00:34:52,440 --> 00:34:55,500 当你看到这样的疯狂的东西,每当你看到一个段故障, 413 00:34:55,500 --> 00:34:59,800 特别是如果你有一个程序,使用多只主, 414 00:34:59,800 --> 00:35:03,570 使用的回溯。 415 00:35:03,570 --> 00:35:13,080 我写BT,而不是全面的回溯字缩写回溯。 416 00:35:13,080 --> 00:35:16,510 但是,夏洛特,当你键入BT并按下回车键,会发生什么呢? 417 00:35:16,510 --> 00:35:23,200 夏洛特它让我看到两行,0行和第1行。 418 00:35:23,200 --> 00:35:26,150 >>呀。所以第0行和第1行。 419 00:35:26,150 --> 00:35:34,560 这些是实际的栈帧目前在播放的时候,你的程序崩溃。 420 00:35:34,560 --> 00:35:42,230 从最上面的帧,帧0开始,并持续到最底层的,这是第1帧。 421 00:35:42,230 --> 00:35:45,140 我们的最高框架是strcmp的框架,。 422 00:35:45,140 --> 00:35:50,080 你可以认为这是类似的问题,我们只是在做测验的指针, 423 00:35:50,080 --> 00:35:54,890 我们交换堆栈帧上的主栈帧的顶部, 424 00:35:54,890 --> 00:35:59,700 和我们的互换使用,主要使用的变量的顶部上的变量。 425 00:35:59,700 --> 00:36:08,440 在这里我们的崩溃发生在我们的strcmp函数,这被称为我们的主要功能, 426 00:36:08,440 --> 00:36:14,370 回溯,不仅给我们的功能在里面的东西没有, 427 00:36:14,370 --> 00:36:16,440 但它也告诉我们,这里的一切,被称为。 428 00:36:16,440 --> 00:36:18,830 所以,如果我多一点点滚动的权利, 429 00:36:18,830 --> 00:36:26,110 我们可以看到,是的,我们在这个的strcmp,sse4.s文件的254行。 430 00:36:26,110 --> 00:36:32,540 但有人呼吁在buggy1.c,6号线。 431 00:36:32,540 --> 00:36:35,960 因此,这意味着我们可以做的 - 是我们可以去看看,看看发生了什么事 432 00:36:35,960 --> 00:36:39,930 在6号线buggy1.c。 433 00:36:39,930 --> 00:36:43,780 同样,也有几种方法可以做到这一点。一个是退出GDB 434 00:36:43,780 --> 00:36:49,460 或者你的代码在另一个窗口中打开和交叉引用。 435 00:36:49,460 --> 00:36:54,740 ,其本身而言,是非常方便的,因为现在,如果你在办公时间 436 00:36:54,740 --> 00:36:57,220 和你已经有了一个段故障和TF想知道一切都被打破, 437 00:36:57,220 --> 00:36:59,710 你可以说,“哦,第6行,我不知道这是怎么回事, 438 00:36:59,710 --> 00:37:03,670 但6号线是什么导致我的计划打破。“ 439 00:37:03,670 --> 00:37:10,430 做到这一点的另一种方法是,你可以使用此命令列表中GDB。 440 00:37:10,430 --> 00:37:13,650 您也可以将其缩写为升。 441 00:37:13,650 --> 00:37:18,910 因此,如果我们打升,我们在这里吗? 442 00:37:18,910 --> 00:37:21,160 我们得到了一大堆怪异的东西。 443 00:37:21,160 --> 00:37:26,030 这是实际的汇编代码 444 00:37:26,030 --> 00:37:29,860 是在strcmp_sse4_2。 445 00:37:29,860 --> 00:37:32,440 这看起来有点时髦的, 446 00:37:32,440 --> 00:37:36,520 的原因,我们正在这是,因为现在, 447 00:37:36,520 --> 00:37:40,160 GDB我们在第0帧。 448 00:37:40,160 --> 00:37:43,070 >> 所以,任何时候我们看变量,任何时候我们来看看源代码, 449 00:37:43,070 --> 00:37:50,530 我们正在寻找源代码的堆栈帧,涉及到我们目前所在 450 00:37:50,530 --> 00:37:53,200 因此,为了获得什么有意义的事情,我们必须 451 00:37:53,200 --> 00:37:57,070 转移到更有意义的栈帧。 452 00:37:57,070 --> 00:38:00,180 在这种情况下,主要的堆栈帧将使一些更有意义, 453 00:38:00,180 --> 00:38:02,680 因为这实际上是我们写的代码。 454 00:38:02,680 --> 00:38:05,330 strcmp的代码。 455 00:38:05,330 --> 00:38:08,650 的方式,可以在帧之间移动,在这种情况下,因为我们有两个, 456 00:38:08,650 --> 00:38:10,430 我们有0和1, 457 00:38:10,430 --> 00:38:13,650 你这样做的up和down命令。 458 00:38:13,650 --> 00:38:18,480 如果我把一帧, 459 00:38:18,480 --> 00:38:21,770 现在,我在主堆栈帧。 460 00:38:21,770 --> 00:38:24,330 我可以向下移动到的地方,我回去, 461 00:38:24,330 --> 00:38:32,830 再上去,走了,再上去。 462 00:38:32,830 --> 00:38:39,750 如果你这样做你的程序在GDB,你得到一个崩溃时,你会得到回溯, 463 00:38:39,750 --> 00:38:42,380 你看到它的一些文件,你不知道发生了什么事情。 464 00:38:42,380 --> 00:38:45,460 您尝试列表,代码并不很熟悉你, 465 00:38:45,460 --> 00:38:48,150 一起来看看在您的框架,并找出你在哪里。 466 00:38:48,150 --> 00:38:51,010 你可能在错误的堆栈帧。 467 00:38:51,010 --> 00:38:58,760 或者至少,你是不是你真的可以调试堆栈帧中。 468 00:38:58,760 --> 00:39:03,110 现在,我们在适当的堆栈帧,我们在主, 469 00:39:03,110 --> 00:39:08,100 现在我们可以使用list命令可以计算出该行是什么。 470 00:39:08,100 --> 00:39:13,590 你也可以看到它,它打印我们就在这里。 471 00:39:13,590 --> 00:39:19,470 但是,我们可以打列出所有相同,列表,为我们提供了这个漂亮的打印输出 472 00:39:19,470 --> 00:39:23,920 实际的源代码,在这里是怎么回事。 473 00:39:23,920 --> 00:39:26,420 >> 特别是,我们可以看一下在第6行。 474 00:39:26,420 --> 00:39:29,330 我们可以看到,在这里发生了什么事情。 475 00:39:29,330 --> 00:39:31,250 它看起来像我们正在做的字符串比较 476 00:39:31,250 --> 00:39:41,050 字符串“CS50石头”和argv [1]。 477 00:39:41,050 --> 00:39:45,700 他到这里来是崩溃。 478 00:39:45,700 --> 00:39:54,120 所以,大小姐,你有什么想法什么可能会在这里? 479 00:39:54,120 --> 00:39:59,400 [大小姐]我不知道为什么它的崩溃。 >>你不知道为什么它的崩溃? 480 00:39:59,400 --> 00:40:02,700 吉米,有什么想法吗? 481 00:40:02,700 --> 00:40:06,240 [麦]我不能完全肯定,但我们最后一次使用的字符串比较, 482 00:40:06,240 --> 00:40:10,260 或strcmp的,我们有三种不同的情况下。 483 00:40:10,260 --> 00:40:12,800 我们没有一个==,我​​不认为,正确的,第一行。 484 00:40:12,800 --> 00:40:16,700 相反,它被分离成三个,一个是== 0, 485 00:40:16,700 --> 00:40:19,910 1 <0,我觉得,一个是> 0。 486 00:40:19,910 --> 00:40:22,590 因此,也许这样的事情? >>呀。所以有这个问题 487 00:40:22,590 --> 00:40:27,200 我们做的比较正确? 488 00:40:27,200 --> 00:40:31,660 Stella吗?有什么想法? 489 00:40:31,660 --> 00:40:38,110 斯特拉]我不知道。 >>不知道。丹尼尔?思考?好吧。 490 00:40:38,110 --> 00:40:44,770 事实证明,所发生的事情在这里是当我们运行程序时, 491 00:40:44,770 --> 00:40:48,370 我们得到了赛格故障,当你第一次运行的程序,丹尼尔, 492 00:40:48,370 --> 00:40:50,800 你给它任何命令行参数? 493 00:40:50,800 --> 00:40:58,420 [丹尼尔]第>>号在这种情况下,是什么时,argv [1]? 494 00:40:58,420 --> 00:41:00,920 >>没有任何价值。 “没错。 495 00:41:00,920 --> 00:41:06,120 那么,有没有适当的字符串值。 496 00:41:06,120 --> 00:41:10,780 但有一定的价值。被存储在那里的​​价值是什么? 497 00:41:10,780 --> 00:41:15,130 >> A垃圾的价值呢? >>它要么是一个垃圾值,或者,在这种情况下, 498 00:41:15,130 --> 00:41:19,930 argv数组结束始终与null的终止。 499 00:41:19,930 --> 00:41:26,050 所以实际上存储在那里是空的。 500 00:41:26,050 --> 00:41:30,810 另一种方法解决这个问题,而不是想着它通过, 501 00:41:30,810 --> 00:41:33,420 是要设法把它打印出来。 502 00:41:33,420 --> 00:41:35,880 这是我说的,使用GDB是伟大的, 503 00:41:35,880 --> 00:41:40,640 因为你可以打印出所有的变量,所有你想要的值 504 00:41:40,640 --> 00:41:43,230 使用这种方便,花花公子p命令。 505 00:41:43,230 --> 00:41:48,520 所以,如果我键入p,然后我输入一个变量的值或变量的名称, 506 00:41:48,520 --> 00:41:55,320 说是argc,我看到argc是1。 507 00:41:55,320 --> 00:42:01,830 如果我想打印出来的argv [0],我可以这样做,就这样。 508 00:42:01,830 --> 00:42:04,840 就像我们所看到的,ARGV [0]是你的程序的名称, 509 00:42:04,840 --> 00:42:06,910 总是的可执行文件的名称。 510 00:42:06,910 --> 00:42:09,740 这里你可以看到它有完整的路径名。 511 00:42:09,740 --> 00:42:15,920 我还可以打印出的argv [1],看看会发生什么。 512 00:42:15,920 --> 00:42:20,890 >> 在这里,我们得到了这种神秘的值。 513 00:42:20,890 --> 00:42:23,890 我们拿到的这款0x0的。 514 00:42:23,890 --> 00:42:27,850 记得在学期开始时,我们谈到十六进制数字吗? 515 00:42:27,850 --> 00:42:34,680 或者说,在年底的pset 0的小问题如何表示十六进制50? 516 00:42:34,680 --> 00:42:39,410 我们编写的十六进制数在CS,只是为了不混淆自己 517 00:42:39,410 --> 00:42:46,080 带小数点的数字,是我们始终以0x前缀。 518 00:42:46,080 --> 00:42:51,420 因此,这0x前缀始终只是解释为十六进制数, 519 00:42:51,420 --> 00:42:57,400 而不是字符串,而不是作为一个十进制数,而不是作为一个二进制数。 520 00:42:57,400 --> 00:43:02,820 由于5-0是一个有效的十六进制数。 521 00:43:02,820 --> 00:43:06,240 这是一个十进制数,50。 522 00:43:06,240 --> 00:43:10,050 因此,这是我们如何消除歧义。 523 00:43:10,050 --> 00:43:14,860 因此,0x0的装置,十六进制的0,这也是十进制0,二进制0。 524 00:43:14,860 --> 00:43:17,030 只是,它的值为0。 525 00:43:17,030 --> 00:43:22,630 事实证明,这是空的,其实是在内存中。 526 00:43:22,630 --> 00:43:25,940 NULL就是0。 527 00:43:25,940 --> 00:43:37,010 在这里,存储元件的argv [1]为null。 528 00:43:37,010 --> 00:43:45,220 因此,我们试图比较“CS50石头”字符串为空字符串。 529 00:43:45,220 --> 00:43:48,130 因此,提领空,试图访问空的东西, 530 00:43:48,130 --> 00:43:55,050 通常会造成某种分割故障或其他不好的事情发生。 531 00:43:55,050 --> 00:43:59,350 而事实证明,STRCMP不请检查 532 00:43:59,350 --> 00:44:04,340 您是否已经通过了一个值,该值是空的。 533 00:44:04,340 --> 00:44:06,370 相反,它只是前进,试图做的事情, 534 00:44:06,370 --> 00:44:14,640 ,如果段故障,段故障,这是你的问题。你必须去解决它。 535 00:44:14,640 --> 00:44:19,730 真的很快,我们会如何解决这个问题?夏洛特? 536 00:44:19,730 --> 00:44:23,540 [夏洛特,您可以检查使用的话。 537 00:44:23,540 --> 00:44:32,240 因此,如果argv [1]为null == 0,则返回1,什么不知所云。 538 00:44:32,240 --> 00:44:34,590 >>呀。所以这是一个伟大的方式来做到这一点,我们可以查看, 539 00:44:34,590 --> 00:44:39,230 我们的价值传递到strcmp的,ARGV [1],它是空? 540 00:44:39,230 --> 00:44:45,830 如果是null,那么我们可以这样说好了,中止。 541 00:44:45,830 --> 00:44:49,450 >> 更常见的方式做,这是使用的argc的值。 542 00:44:49,450 --> 00:44:52,040 在这里你可以看到在开始的主, 543 00:44:52,040 --> 00:44:58,040 我们省略了,第一,我们通常做测试时,我们使用命令行参数, 544 00:44:58,040 --> 00:45:05,240 这是我们ARGC值测试是否是我们所期望的。 545 00:45:05,240 --> 00:45:10,290 在这种情况下,我们预计至少需要两个参数, 546 00:45:10,290 --> 00:45:13,660 的程序名加一个其它。 547 00:45:13,660 --> 00:45:17,140 因为我们使用的第二个参数在这里。 548 00:45:17,140 --> 00:45:21,350 因此,有一些测试的结果之前,在我们的strcmp呼叫 549 00:45:21,350 --> 00:45:37,390 argv是,测试是否至少2个,也做了同样的事情。 550 00:45:37,390 --> 00:45:40,620 我们可以看到,如果再次运行程序。 551 00:45:40,620 --> 00:45:45,610 您可以随时重新启动你的程序在GDB中,这是非常好的。 552 00:45:45,610 --> 00:45:49,310 您可以运行,而当你传递参数给你的程序, 553 00:45:49,310 --> 00:45:53,060 通过他们,当你调用运行,而不是当你启动GDB。 554 00:45:53,060 --> 00:45:57,120 这样,你可以每次都用不同的参数调用程序。 555 00:45:57,120 --> 00:46:08,080 所以运行,或者,我可以输入r,,让我们看看会发生什么,如果我们输入“你好”。 556 00:46:08,080 --> 00:46:11,140 它总是会问你,如果你想从一开始就再次启动。 557 00:46:11,140 --> 00:46:17,490 通常情况下,你想从一开始就再次启动它。 558 00:46:17,490 --> 00:46:25,010 而在这一点上,它会重新启动一次,它打印出 559 00:46:25,010 --> 00:46:28,920 该程序,我们正在运行,buggy1的说法您好, 560 00:46:28,920 --> 00:46:32,720 并打印出这个标准了,它说,“你得到一个D,”悲伤的脸。 561 00:46:32,720 --> 00:46:37,610 但是,我们不段错误。它说,过程正常退出。 562 00:46:37,610 --> 00:46:39,900 所以,看起来还不错。 563 00:46:39,900 --> 00:46:43,050 没有更多的赛格故障,我们做了过去, 564 00:46:43,050 --> 00:46:48,190 所以看起来这的确是段故障错误,我们得到。 565 00:46:48,190 --> 00:46:51,540 不幸的是,它告诉我们,我们需要一个D. 566 00:46:51,540 --> 00:46:54,090 >> 我们可以回去看一下代码,看看在那里发生了什么事情 567 00:46:54,090 --> 00:46:57,980 要弄清楚什么是 - 为什么它告诉我们,我们得到了D. 568 00:46:57,980 --> 00:47:03,690 让我们来看看,这里被这个printf说,你有D. 569 00:47:03,690 --> 00:47:08,540 如果我们输入列表,你继续输入列表,它不断迭代,通过你们的节目, 570 00:47:08,540 --> 00:47:10,940 因此,它会告诉你你的程序的前几行。 571 00:47:10,940 --> 00:47:15,450 然后,它会告诉你在接下来的几行字,下一个块和下一块。 572 00:47:15,450 --> 00:47:18,240 它会继续尝试下去。 573 00:47:18,240 --> 00:47:21,180 现在,我们会得到“线16号超出范围。” 574 00:47:21,180 --> 00:47:23,940 因为它只有15行。 575 00:47:23,940 --> 00:47:30,310 如果你到了这一点,你想知道,我该怎么办?“你可以用help命令。 576 00:47:30,310 --> 00:47:34,340 使用帮助“,然后给它一个命令的名称。 577 00:47:34,340 --> 00:47:36,460 你看到GDB为我们提供了这样的东西。 578 00:47:36,460 --> 00:47:43,870 它说,“不带参数,列出了10个多行后或以前上市。 579 00:47:43,870 --> 00:47:47,920 列表 - 列出了前十行 - “ 580 00:47:47,920 --> 00:47:52,960 所以,让我们尝试使用列表中减去。 581 00:47:52,960 --> 00:47:57,000 这列出的前10行,你可以玩弄名单一点点。 582 00:47:57,000 --> 00:48:02,330 你可以做的列表,列表 - ,你甚至可以给列出一个数字,如清单8, 583 00:48:02,330 --> 00:48:07,500 ,它就会列出了10条线,8号线附近。 584 00:48:07,500 --> 00:48:10,290 你可以看到在这里发生了什么事,是你已经有了一个简单的if else。 585 00:48:10,290 --> 00:48:13,980 如果您键入在CS50岩石,它会打印出“你会得到一个A.” 586 00:48:13,980 --> 00:48:16,530 否则,它会打印出“你得到了D.” 587 00:48:16,530 --> 00:48:23,770 游民镇。好的。是吗? 588 00:48:23,770 --> 00:48:26,730 >> [丹尼尔]因此,当我试图这样做CS50不带引号的岩石, 589 00:48:26,730 --> 00:48:29,290 它说:“你得到了D.” 590 00:48:29,290 --> 00:48:32,560 我需要引号来得到它的工作,这是为什么? 591 00:48:32,560 --> 00:48:38,490 >>呀。事实证明,当 - 这是另一个有趣的小花絮 - 592 00:48:38,490 --> 00:48:47,900 当您运行该程序,如果我们运行和输入CS50岩石, 593 00:48:47,900 --> 00:48:50,800 就像丹尼尔说他这样做,按Enter键, 594 00:48:50,800 --> 00:48:52,870 它说,我们得到了D. 595 00:48:52,870 --> 00:48:55,580 而问题是,这是为什么? 596 00:48:55,580 --> 00:49:02,120 而事实证明,我们的终端和的GDB解析这些作为两个单独的参数。 597 00:49:02,120 --> 00:49:04,800 因为当有一个空间,这暗示 598 00:49:04,800 --> 00:49:08,730 第一个参数结束,即将开始的下一个参数。 599 00:49:08,730 --> 00:49:13,260 结合的方式一分为二,或遗憾的是,成为一个参数, 600 00:49:13,260 --> 00:49:18,510 使用引号。 601 00:49:18,510 --> 00:49:29,560 所以现在,如果我们把它放在引号并再次运行它,我们可以得到一个A。 602 00:49:29,560 --> 00:49:38,780 因此,只要回顾一下,没有引号,的CS50和岩石被解析为两个独立的参数。 603 00:49:38,780 --> 00:49:45,320 随着行情中,它作为一个参数完全解析。 604 00:49:45,320 --> 00:49:53,070 >> 我们可以看到这一个断点。 605 00:49:53,070 --> 00:49:54,920 到目前为止,我们已经运行我们的程序,它的运行 606 00:49:54,920 --> 00:49:58,230 直到它赛格故障或点击错误 607 00:49:58,230 --> 00:50:05,930 或直到它已退出所有已经完全正常。 608 00:50:05,930 --> 00:50:08,360 这并不一定是最有帮助的事情,因为有时 609 00:50:08,360 --> 00:50:11,840 你有一个错误在你的程序中,但它不是一个分割故障导致。 610 00:50:11,840 --> 00:50:16,950 它不会导致程序停止或类似的东西。 611 00:50:16,950 --> 00:50:20,730 GDB的方式来获得你的程序在一个特定的点暂停 612 00:50:20,730 --> 00:50:23,260 是设置一个断点。 613 00:50:23,260 --> 00:50:26,520 你可以做到这一点,一个函数名上设置一个断点 614 00:50:26,520 --> 00:50:30,770 或者您可以设置一个特定的代码行上设置一个断点。 615 00:50:30,770 --> 00:50:34,450 我想设置断点的函数名,因为 - 简单易记, 616 00:50:34,450 --> 00:50:37,700 如果你真的一点点去改变你的源代码, 617 00:50:37,700 --> 00:50:42,020 那么你的断点将留在你的代码在同一个地方。 618 00:50:42,020 --> 00:50:44,760 然而,如果你使用的是行号,行号更改 619 00:50:44,760 --> 00:50:51,740 因为你添加或删除了一些代码,然后断点都完全搞砸了。 620 00:50:51,740 --> 00:50:58,590 其中最常见的事情,我做的是设置一个断点上的主要功能。 621 00:50:58,590 --> 00:51:05,300 我经常会启动GDB,我会键入b主,按下回车键,将设置一个断点 622 00:51:05,300 --> 00:51:10,630 的主要功能,只是说,“暂停程序尽快开始运行,” 623 00:51:10,630 --> 00:51:17,960 这样一来,当我运行我的程序,比方说,CS50岩石的两个参数 624 00:51:17,960 --> 00:51:24,830 并按下回车键,得到的主要功能,它就停在第一线, 625 00:51:24,830 --> 00:51:30,620 前strcmp函数求值。 626 00:51:30,620 --> 00:51:34,940 >> 由于我停顿了一下,现在我可以开始摆弄周围,看是怎么回事 627 00:51:34,940 --> 00:51:40,250 所有的不同的变量传递到我的程序。 628 00:51:40,250 --> 00:51:43,670 在这里,我可以打印出argc和看看会发生什么。 629 00:51:43,670 --> 00:51:50,030 请参阅argc是3,因为它有3个不同的值。 630 00:51:50,030 --> 00:51:54,060 它得名的程序,它得到的第一个参数,第二个参数。 631 00:51:54,060 --> 00:52:09,330 我们可以打印在ARGV [0],ARGV [1]和argv [2]。 632 00:52:09,330 --> 00:52:12,030 所以,现在你也可以看到为什么这STRCMP调用是要失败的, 633 00:52:12,030 --> 00:52:21,650 因为你看到,它并分裂成两个独立的参数的CS50和岩石。 634 00:52:21,650 --> 00:52:27,250 在这一点上,一旦你已经打了断点,你可以通过你的程序继续加强 635 00:52:27,250 --> 00:52:32,920 一行行,而不是重新启动程序。 636 00:52:32,920 --> 00:52:35,520 所以,如果你不想再次启动程序,只是继续从这里开始, 637 00:52:35,520 --> 00:52:41,970 您可以使用continue命令,并继续将运行该程序的结束。 638 00:52:41,970 --> 00:52:45,010 就像它在这里做的。 639 00:52:45,010 --> 00:52:54,880 不过,如果我重新启动程序,CS50岩石,它打我的断点再次, 640 00:52:54,880 --> 00:52:59,670 而这一次,如果我不想去,一路过关斩将,其余的程序, 641 00:52:59,670 --> 00:53:08,040 我可以用下一个命令,这也是我缩写与n。 642 00:53:08,040 --> 00:53:12,960 而这将逐步的程序行线。 643 00:53:12,960 --> 00:53:17,530 所以,你可以看的东西执行,作为变量的变化,事情得到更新。 644 00:53:17,530 --> 00:53:21,550 这是相当不错的。 645 00:53:21,550 --> 00:53:26,570 其他很酷的事情,而不是一遍又一遍,再重复同样的命令, 646 00:53:26,570 --> 00:53:30,670 所以在这里,如果你只需要敲击回车 - 你看,我没有在任何输入 - 647 00:53:30,670 --> 00:53:33,780 如果我只是打回车键,将重复前面的命令, 648 00:53:33,780 --> 00:53:36,900 或以前的GDB命令,我刚刚放进去。 649 00:53:36,900 --> 00:53:56,000 我可以保持按下回车键,它会通过我的代码行的行不断加强。 650 00:53:56,000 --> 00:53:59,310 我会鼓励你们去看看其他bug的程序。 651 00:53:59,310 --> 00:54:01,330 我们没有时间去通过所有这些今天在第。 652 00:54:01,330 --> 00:54:05,890 源代码是存在的,所以你可以看到这是怎么回事 653 00:54:05,890 --> 00:54:07,730 如果你真的卡幕后, 654 00:54:07,730 --> 00:54:11,940 但最起码,练习启动GDB, 655 00:54:11,940 --> 00:54:13,940 运行该程序,直到它打破了你, 656 00:54:13,940 --> 00:54:18,260 回溯,搞清楚什么样的功能崩溃, 657 00:54:18,260 --> 00:54:24,450 哪一行,打印出一些变量的值, 658 00:54:24,450 --> 00:54:30,140 只是让你的感觉,因为这将真正帮助你前进。 659 00:54:30,140 --> 00:54:36,340 在这一点上,我们要退出的GDB,你退出或者只是q。 660 00:54:36,340 --> 00:54:40,460 如果你的程序的运行仍然在中间,并没有退出, 661 00:54:40,460 --> 00:54:43,510 它总是会问你:“你确定你真的要退出吗?” 662 00:54:43,510 --> 00:54:48,770 您可以打是肯定的。 663 00:54:48,770 --> 00:54:55,250 >> 现在,我们要看看我们的下一个问题,这是猫的程序。 664 00:54:55,250 --> 00:54:59,880 如果你看重定向和管道短,你会看到汤米使用这个程序 665 00:54:59,880 --> 00:55:07,540 基本上所有输出的文件打印到屏幕上。 666 00:55:07,540 --> 00:55:12,660 所以,如果我遇到猫,这实际上是一个内置的程序到设备, 667 00:55:12,660 --> 00:55:16,860 ,如果你有Mac电脑,你可以做到这一点在您的Mac上,如果你打开​​终端。 668 00:55:16,860 --> 00:55:25,630 而我们 - 猫,比方说,cp.c,并按下回车键。 669 00:55:25,630 --> 00:55:29,640 这是什么做的,如果我们向上滚动一点点,看到我们跑线, 670 00:55:29,640 --> 00:55:40,440 我们跑了cat命令,它实际上只是打印出来的内容cp.c我们的屏幕上。 671 00:55:40,440 --> 00:55:44,140 我们可以再次运行它,你可以把多个文件一起。 672 00:55:44,140 --> 00:55:49,880 所以,你可以做猫cp.c,那么我们也可以连接起来的cat.c的文件, 673 00:55:49,880 --> 00:55:53,250 这是我们写的程序, 674 00:55:53,250 --> 00:55:58,140 它会同时打印文件备份到我们的屏幕上。 675 00:55:58,140 --> 00:56:05,490 因此,如果我们向上滚动一点点,我们看到,当我们跑了这猫cp.c,cat.c, 676 00:56:05,490 --> 00:56:17,110 首先它打印出来cp文件,然后在下面的,它打印出cat.c的文件就在这里。 677 00:56:17,110 --> 00:56:19,650 我们将用此来只是让我们的脚湿。 678 00:56:19,650 --> 00:56:25,930 玩简单的打印到终端,请参阅如何工作的。 679 00:56:25,930 --> 00:56:39,170 如果你们打开用gedit cat.c,按Enter键, 680 00:56:39,170 --> 00:56:43,760 你可以看到我们写的程序。 681 00:56:43,760 --> 00:56:48,980 我们这个漂亮的锅炉板,所以我们不必花时间打字了这一切。 682 00:56:48,980 --> 00:56:52,310 我们还检查传入的参数 683 00:56:52,310 --> 00:56:56,910 我们打​​印出不错的用法消息。 684 00:56:56,910 --> 00:57:00,950 >> 这是诸如此类的事情,再次,就像我们一直在谈论, 685 00:57:00,950 --> 00:57:04,490 这几乎就像肌肉记忆。 686 00:57:04,490 --> 00:57:07,190 只记得继续做同样的东西 687 00:57:07,190 --> 00:57:11,310 总是打印出某种有用的消息 688 00:57:11,310 --> 00:57:17,670 让人们知道如何运行你的程序。 689 00:57:17,670 --> 00:57:21,630 随着猫,它是非常简单的,我们只是要通过不同的参数 690 00:57:21,630 --> 00:57:24,300 被传递到我们的计划,我们要打印 691 00:57:24,300 --> 00:57:29,950 其内容在一个时间的一个屏幕。 692 00:57:29,950 --> 00:57:35,670 为了打印文件输出到屏幕上,我们要做的东西非常相似 693 00:57:35,670 --> 00:57:38,120 我们所做的,在测验结束时。 694 00:57:38,120 --> 00:57:45,350 在测验结束,聘用程序,我们要打开一个文件, 695 00:57:45,350 --> 00:57:48,490 然后我们有打印到它。 696 00:57:48,490 --> 00:57:54,660 在这种情况下,我们要打开一个文件,我们要读取它,而不是。 697 00:57:54,660 --> 00:58:00,630 然后我们要打印的,而不是到一个文件中,我们要打印到屏幕上。 698 00:58:00,630 --> 00:58:05,830 所以打印到屏幕上,你用printf之前全部完成。 699 00:58:05,830 --> 00:58:08,290 所以这是不是太疯狂了。 700 00:58:08,290 --> 00:58:12,190 但是,读取一个文件是一种奇怪的。 701 00:58:12,190 --> 00:58:17,300 我们将通过那么一点点的时间。 702 00:58:17,300 --> 00:58:20,560 如果你们回去,最后一个问题,您的测验,问题33, 703 00:58:20,560 --> 00:58:27,280 我们要在这里做的第一行,打开文件,非常类似于我们在那里做了什么。 704 00:58:27,280 --> 00:58:36,370 因此,斯特拉,是什么样子,当我们打开一个文件时,该行吗? 705 00:58:36,370 --> 00:58:47,510 [特拉]资本FILE *文件 - “”好了。 >> - 是平等的FOPEN。 “是啊。 706 00:58:47,510 --> 00:58:55,980 在这种情况下是什么?这是在注释中。 707 00:58:55,980 --> 00:59:06,930 >>这是在评论吗?的argv [i]的和r? 708 00:59:06,930 --> 00:59:11,300 “没错。正确的。斯特拉是完全正确的。 709 00:59:11,300 --> 00:59:13,720 这是行看起来像什么。 710 00:59:13,720 --> 00:59:19,670 我们将得到一个文件流变量,它存储在一个FILE *,所以全部大写字母, 711 00:59:19,670 --> 00:59:25,720 将文件FILE *,这个变量的名称。 712 00:59:25,720 --> 00:59:32,250 无论我们喜欢,我们可以把它称为。我们可以把它first_file,或file_i的,无论我们想要的。 713 00:59:32,250 --> 00:59:37,590 在命令行上这个节目,然后通过的文件名。 714 00:59:37,590 --> 00:59:44,450 因此,它存储在argv [我],然后我们要打开这个文件在读取模式。 715 00:59:44,450 --> 00:59:48,100 现在,我们已经打开了文件,有什么事情,我们总是要记住做 716 00:59:48,100 --> 00:59:52,230 每当我们打开一个文件?关闭它。 717 00:59:52,230 --> 00:59:57,220 所以,大小姐,我们该如何关闭一个文件? 718 00:59:57,220 --> 01:00:01,020 [大小姐] FCLOSE(文件)>> FCLOSE(文件)。没错。 719 01:00:01,020 --> 01:00:05,340 大。好吧。如果我们看一下在这里做评论, 720 01:00:05,340 --> 01:00:11,940 它说,“开放的argv [i]和它的内容打印到stdout。” 721 01:00:11,940 --> 01:00:15,460 >> 标准输出是一个奇怪的名字。 stdout是我们的一种说法 722 01:00:15,460 --> 01:00:22,880 我们要打印到终端,我们希望将其打印到标准输出流。 723 01:00:22,880 --> 01:00:26,450 事实上,我们可以摆脱这个评论就在这里。 724 01:00:26,450 --> 01:00:36,480 我要复制并粘贴它,因为这是我们所做的。 725 01:00:36,480 --> 01:00:41,290 在这一点上,现在我们要读取的文件的点点滴滴。 726 01:00:41,290 --> 01:00:46,300 我们已经讨论了一对夫妇的读取文件的方式。 727 01:00:46,300 --> 01:00:51,830 哪些是你最喜欢这么远吗? 728 01:00:51,830 --> 01:00:57,960 哪种方式,你见过,你还记得,读文件? 729 01:00:57,960 --> 01:01:04,870 [丹尼尔·弗里德? >>弗里德?因此,弗里德是其中之一。吉米,你知不知道什么人吗? 730 01:01:04,870 --> 01:01:12,150 [麦]第>>好。不。夏洛特?亚历山大?还有其他人吗?好吧。 731 01:01:12,150 --> 01:01:20,740 因此,其他的是fgetc,是我们将使用一个不少。 732 01:01:20,740 --> 01:01:26,410 还有fscanf,你们看到这里的模式吗? 733 01:01:26,410 --> 01:01:29,170 他们都以f。任何一个文件。 734 01:01:29,170 --> 01:01:35,260 有FREAD,fgetc函数,fscanf。这些所有的读出功能。 735 01:01:35,260 --> 01:01:49,120 为写我们FWRITE,我们的fputc,而不是fgetc函数。 736 01:01:49,120 --> 01:01:58,250 我们也fprintf,像我们看到的测验。 737 01:01:58,250 --> 01:02:01,680 由于这是一个问题,涉及从文件中读取, 738 01:02:01,680 --> 01:02:04,940 我们将使用这三个功能中的一个。 739 01:02:04,940 --> 01:02:10,890 我们不打算使用这些功能。 740 01:02:10,890 --> 01:02:14,880 这些功能都在标准I / O库。 741 01:02:14,880 --> 01:02:17,510 所以,如果你看一下这个程序的顶部, 742 01:02:17,510 --> 01:02:24,110 你可以看到,我们已经包含标准I / O库的头文件。 743 01:02:24,110 --> 01:02:27,120 如果我们想弄清楚,我们要使用哪一个, 744 01:02:27,120 --> 01:02:29,690 我们可以随时打开的手册页。 745 01:02:29,690 --> 01:02:34,350 因此,我们可以输入man的stdio 746 01:02:34,350 --> 01:02:43,180 并阅读所有的的stdio的输入和输出功能C. 747 01:02:43,180 --> 01:02:49,870 我们已经可以看到啊,看看。提fgetc,它的一提的fputc。 748 01:02:49,870 --> 01:02:57,220 所以,你可以向下一点点,看,说,fgetc函数 749 01:02:57,220 --> 01:03:00,060 看它的man page。 750 01:03:00,060 --> 01:03:03,430 你可以看到,它伴随着一大堆的其他功能: 751 01:03:03,430 --> 01:03:12,640 fgetc函数,FGETS,getc函数,getchar函数,获取,ungetc的函数,其输入的字符和字符串。 752 01:03:12,640 --> 01:03:19,180 因此,这是我们如何从标准输入文件读取的字符和字符串, 753 01:03:19,180 --> 01:03:21,990 这本质上是从用户。 754 01:03:21,990 --> 01:03:24,780 这是如何做到这一点,在实际C. 755 01:03:24,780 --> 01:03:30,850 因此,这是不使用的GetString和getchar功能 756 01:03:30,850 --> 01:03:36,840 我们使用的从CS50库。 757 01:03:36,840 --> 01:03:39,710 我们要做的这个问题的几种方法 758 01:03:39,710 --> 01:03:43,430 所以,你可以看到两种不同的方法做这件事。 759 01:03:43,430 --> 01:03:48,490 fread函数,丹尼尔提到的和fgetc函数都不错的办法做到这一点。 760 01:03:48,490 --> 01:03:53,790 我认为fgetc函数是一个容易一些,因为它只有你可以看到, 761 01:03:53,790 --> 01:03:59,660 一个参数,FILE *,我们试图读取的字符, 762 01:03:59,660 --> 01:04:02,740 它的返回值是一个int。 763 01:04:02,740 --> 01:04:05,610 这是一个有点混乱,对吗? 764 01:04:05,610 --> 01:04:11,450 >> 因为我们得到一个字符,那么为什么没有这个返回一个字符? 765 01:04:11,450 --> 01:04:18,700 为什么这可能不会返回一个字符,你们有什么想法? 766 01:04:18,700 --> 01:04:25,510 [的大小姐答案,不知所云] >>呀。所以大小姐是完全正确的。 767 01:04:25,510 --> 01:04:31,570 如果它是ASCII,那么这个整数被映射到一个实际的字符。 768 01:04:31,570 --> 01:04:33,520 可能是一个ASCII字符,这是正确的。 769 01:04:33,520 --> 01:04:36,220 这到底发生了什么。 770 01:04:36,220 --> 01:04:39,190 我们使用的是一个int,只是因为它有更多的比特。 771 01:04:39,190 --> 01:04:44,750 这是更大的不是一个字符的字符只有8位,32位机,1个字节。 772 01:04:44,750 --> 01:04:48,520 和一个int的所有4个字节的空间价值。 773 01:04:48,520 --> 01:04:50,940 事实证明,fgetc函数的工作方式, 774 01:04:50,940 --> 01:04:53,940 如果我们向下滚动本手册页一点点,在我们的简介 775 01:04:53,940 --> 01:05:05,000 向下滚动的方式。原来,他们用这种特殊的值EOF。 776 01:05:05,000 --> 01:05:09,640 这是一个特殊的常量,fgetc函数的返回值 777 01:05:09,640 --> 01:05:14,570 只要你打的文件,或者如果你得到一个错误。 778 01:05:14,570 --> 01:05:18,170 事实证明,做这些比较EOF正确的, 779 01:05:18,170 --> 01:05:24,060 你想有额外的大量的信息,你必须在一个int 780 01:05:24,060 --> 01:05:28,420 而不是使用一个char变量。 781 01:05:28,420 --> 01:05:32,130 即使fgetc函数有效地从一个文件中的字符, 782 01:05:32,130 --> 01:05:38,450 你要记住,它返回的东西是你的类型为int。 783 01:05:38,450 --> 01:05:41,360 这就是说,它很容易使用。 784 01:05:41,360 --> 01:05:44,960 它给我们一个字符,所以我们要做的是保持要求的文件, 785 01:05:44,960 --> 01:05:48,440 “给我的下一个字符的下一个字符给我,给我的下一个字符。” 786 01:05:48,440 --> 01:05:51,400 直到我们得到的文件结束。 787 01:05:51,400 --> 01:05:54,730 这会拉一个字符的时间从我们的文件, 788 01:05:54,730 --> 01:05:56,250 然后我们可以做任何我们喜欢它。 789 01:05:56,250 --> 01:06:00,160 我们可以将其存储,我们可以将其添加为一个字符串,我们可以把它打印出来。 790 01:06:00,160 --> 01:06:04,630 任何。 791 01:06:04,630 --> 01:06:09,600 >> 放大退了出去,并要回到我们的cat.c的程序, 792 01:06:09,600 --> 01:06:16,170 如果我们要使用fgetc函数, 793 01:06:16,170 --> 01:06:21,710 我们会如何对待这下一行代码? 794 01:06:21,710 --> 01:06:26,020 我们将使用 - 弗里德会做一些事情略有不同。 795 01:06:26,020 --> 01:06:32,600 而这个时候,我们只是要使用fgetc函数得到一个字符的时间。 796 01:06:32,600 --> 01:06:40,910 为了处理一个完整的文件,我们可能会做什么? 797 01:06:40,910 --> 01:06:44,030 在一个文件中有多少个字符? 798 01:06:44,030 --> 01:06:47,390 有很多。所以,你可能希望得到一个 799 01:06:47,390 --> 01:06:49,860 然后得到另一个获得,并得到另一个。 800 01:06:49,860 --> 01:06:53,330 你认为我们可能要在这里使用的是什么样的算法? 801 01:06:53,330 --> 01:06:55,470 什么类型的 - ? [亚历山大]一个for循环? “没错。 802 01:06:55,470 --> 01:06:57,500 某些类型的循环。 803 01:06:57,500 --> 01:07:03,380 一个for循环实际上是很大的,在这种情况下。 804 01:07:03,380 --> 01:07:08,620 像你说的,这听起来像你想有一个循环,在整个文件, 805 01:07:08,620 --> 01:07:11,820 获得一个字符的时间。 806 01:07:11,820 --> 01:07:13,850 任何建议,可能看起来像什么? 807 01:07:13,850 --> 01:07:22,090 [亚历山大,不知所云] 808 01:07:22,090 --> 01:07:30,050 好了,只是英语告诉我你想要做的是什么? [亚历山大,不知所云] 809 01:07:30,050 --> 01:07:36,270 因此,在这种情况下,这听起来像我们只是试图在整个文件中环。 810 01:07:36,270 --> 01:07:45,330 [亚历山大]所以,我为int的大小吗? >>的大小 - ? 811 01:07:45,330 --> 01:07:49,290 我猜大小的文件,对不对?的大小 - 我们就会把它写这样的。 812 01:07:49,290 --> 01:07:57,470 尺寸文件的时间,我+ +。 813 01:07:57,470 --> 01:08:04,610 因此,原来的方式,你使用fgetc,这是新的, 814 01:08:04,610 --> 01:08:10,460 是,有没有简单的方法得到的文件大小 815 01:08:10,460 --> 01:08:16,979 这个“大小”式的结构,你已经看到过。 816 01:08:16,979 --> 01:08:20,910 当我们在使用,fgetc功能,我们要介绍的某种 817 01:08:20,910 --> 01:08:29,069 新的,时髦的语法for循环,而不是只使用基本的计数器 818 01:08:29,069 --> 01:08:33,920 去逐个字符,我们要拉一个字符的时候, 819 01:08:33,920 --> 01:08:37,120 一次一个字符的方式,以及我们所知道的,我们是在结束 820 01:08:37,120 --> 01:08:41,290 是不是当我们已经数了一定数目的字符, 821 01:08:41,290 --> 01:08:49,939 但是,当我们拉出来的字符是特殊的文件结束符。 822 01:08:49,939 --> 01:08:58,689 因此,我们可以做到这一点 - 我把这种路,我们要初始化 823 01:08:58,689 --> 01:09:08,050 我们的第一次调用出来的文件的第一个字符。 824 01:09:08,050 --> 01:09:14,979 所以这部分就在这里,这是怎么回事字符的文件 825 01:09:14,979 --> 01:09:20,840 并把它存储到变量ch。 826 01:09:20,840 --> 01:09:25,420 我们将继续这样做,直到我们得到的文件, 827 01:09:25,420 --> 01:09:41,170 我们做测试不等于EOF字符,特殊字符。 828 01:09:41,170 --> 01:09:48,750 然后,而不是做CH + +,这将只是增加价值, 829 01:09:48,750 --> 01:09:52,710 因此,如果我们看到一个出来的文件,一个大写的A,说, 830 01:09:52,710 --> 01:09:56,810 CH + +会给我们B,然后我们会得到C,然后按下d。 831 01:09:56,810 --> 01:09:59,310 这显然​​不是我们想要的。我们想在这里 832 01:09:59,310 --> 01:10:05,830 在这最后一点是,我们希望得到的下一个字符的文件。 833 01:10:05,830 --> 01:10:09,500 >> 所以,我们怎么可能得到的下一个字符的文件呢? 834 01:10:09,500 --> 01:10:13,470 我们怎样才能从文件的第一个字符? 835 01:10:13,470 --> 01:10:17,200 [学生]:fgetfile? >> fgetc函数,对不起,你是完全正确的。 836 01:10:17,200 --> 01:10:20,470 我拼错了就在那里。所以,是的。 837 01:10:20,470 --> 01:10:26,240 在这里,而不是做CH + +, 838 01:10:26,240 --> 01:10:29,560 我们只是调用fgetc函数(文件) 839 01:10:29,560 --> 01:10:39,180 和存储相同ch变量的结果在我们的。 840 01:10:39,180 --> 01:10:43,730 [学生提问,不知所云] 841 01:10:43,730 --> 01:10:52,390 >>这是这些FILE *人是特殊的。 842 01:10:52,390 --> 01:10:59,070 他们的工作方式是 - 当你第一次打开 - 当你第一次作出这样的fopen的调用, 843 01:10:59,070 --> 01:11:04,260 有效地作为开头的文件指针FILE *。 844 01:11:04,260 --> 01:11:12,830 然后每次调用fgetc,移动一个字符通过该文件。 845 01:11:12,830 --> 01:11:23,280 所以每当你打电话,你的文件增加一个字符指针。 846 01:11:23,280 --> 01:11:26,210 ,并时,fgetc再次,​​你要搬到另一个字符 847 01:11:26,210 --> 01:11:28,910 另一个字符和其他字符和其他字符。 848 01:11:28,910 --> 01:11:32,030 [学生提问,难以理解] >> that's的 - 是啊。 849 01:11:32,030 --> 01:11:34,810 这是一种这种神奇的引擎盖下。 850 01:11:34,810 --> 01:11:37,930 你不断递增通过。 851 01:11:37,930 --> 01:11:46,510 在这一点上,你能够真正与字符。 852 01:11:46,510 --> 01:11:52,150 因此,我们怎么可能打印出来的画面,现在呢? 853 01:11:52,150 --> 01:11:58,340 我们可以使用相同的printf的事情,我们之前使用。 854 01:11:58,340 --> 01:12:00,330 我们一直在使用一学期。 855 01:12:00,330 --> 01:12:05,450 我们可以调用printf, 856 01:12:05,450 --> 01:12:21,300 我们可以通过在性格就是这样。 857 01:12:21,300 --> 01:12:27,430 做到这一点的另一种方式是使用printf不必做此格式字符串,而不是 858 01:12:27,430 --> 01:12:29,490 我们也可以使用其他的功能之一。 859 01:12:29,490 --> 01:12:40,090 我们可以使用的fputc,可打印字符在屏幕上, 860 01:12:40,090 --> 01:12:52,580 除非我们看的fputc - 让我放大了一点点。 861 01:12:52,580 --> 01:12:56,430 我们看到了什么是好的,是需要我们读出的字符使用fgetc函数, 862 01:12:56,430 --> 01:13:05,100 但后来我们给它一个流打印到。 863 01:13:05,100 --> 01:13:11,850 我们也可以用putchar函数,就可以直接输出到标准输出。 864 01:13:11,850 --> 01:13:16,070 因此,有一大堆不同的选择,我们可以使用的打印。 865 01:13:16,070 --> 01:13:19,580 他们都在标准I / O库。 866 01:13:19,580 --> 01:13:25,150 每当你想打印 - 输出,默认情况下,将打印的特殊标准输出流, 867 01:13:25,150 --> 01:13:27,910 这是标准输出。 868 01:13:27,910 --> 01:13:41,300 因此,我们就可以把它作为一种本魔法值,在这里的stdout。 869 01:13:41,300 --> 01:13:48,410 哎呀。将分号外部。 870 01:13:48,410 --> 01:13:52,790 >> 这是很多新的,时髦的信息在这里。 871 01:13:52,790 --> 01:13:58,600 很多,这是很地道的,在这个意义上,这是代码 872 01:13:58,600 --> 01:14:05,700 是这样写的,只是因为它是干净的,易于阅读。 873 01:14:05,700 --> 01:14:11,520 有很多不同的方式来做到这一点,您可以使用许多不同的功能, 874 01:14:11,520 --> 01:14:14,680 但我们往往只是遵循相同的模式,一遍又一遍。 875 01:14:14,680 --> 01:14:20,180 所以,不要惊讶,如果你看到这样的代码来了一次又一次。 876 01:14:20,180 --> 01:14:25,690 好的。在这一点上,我们需要打破的一天。 877 01:14:25,690 --> 01:14:31,300 感谢您的到来。感谢收看如果你在线。我们会看到你下周。 878 01:14:31,300 --> 01:14:33,890 [CS50.TV]