1 00:00:00,000 --> 00:00:12,410 [音乐] 2 00:00:12,410 --> 00:00:12,830 好的 3 00:00:12,830 --> 00:00:13,370 欢迎回来 4 00:00:13,370 --> 00:00:16,510 这里是CS50 我们继续第一周的课 5 00:00:16,510 --> 00:00:20,050 我们向大家介绍很多科技产品 例如Google眼镜的原因之一 6 00:00:20,050 --> 00:00:24,120 就是它们要用到API 7 00:00:24,120 --> 00:00:25,980 也就是应用程序接口 8 00:00:25,980 --> 00:00:28,830 这就是说 有了正确的文件与记录 9 00:00:28,830 --> 00:00:32,850 你也可以写出给这些产品的软件 10 00:00:32,850 --> 00:00:36,200 所以我们在学期末想要达到的目标就是 11 00:00:36,200 --> 00:00:39,310 如果一个产品正在不断发展 例如 Google眼镜可能会有兴趣 12 00:00:39,310 --> 00:00:42,220 我们会看看能不能借到一些硬件 以及 13 00:00:42,220 --> 00:00:46,500 对公众开放的API 这样你们就可以开始自己做出 14 00:00:46,500 --> 00:00:48,630 可以在Google眼镜中运行的软件了 15 00:00:48,630 --> 00:00:51,710 我们一直玩的另外一个小装置 我们想 16 00:00:51,710 --> 00:00:53,910 在学期末大概会很有意思 就是这个小玩意 17 00:00:53,910 --> 00:00:56,860 它叫做Leap Motion 你们马上将要看到的 18 00:00:56,860 --> 00:01:00,280 是这个产品的广告 但是它能很好地展示 19 00:01:00,280 --> 00:01:01,240 这个产品的功能 20 00:01:01,240 --> 00:01:05,550 它 也一样 有API 而你可以通过API自己写出软件 21 00:01:05,550 --> 00:01:10,545 来控制你的电脑 就像这个一分钟的视频里展示得那样 22 00:01:10,545 --> 00:01:12,070 [播放视频] 23 00:02:08,002 --> 00:02:08,590 [视频结束] 24 00:02:08,590 --> 00:02:11,190 现在 这个装置被放在USB上 25 00:02:11,190 --> 00:02:14,290 你可以将它连到自己的电脑上 不过我想 26 00:02:14,290 --> 00:02:17,930 不久之后 这种技术就应该存在于新一批PC和MAC上了 27 00:02:17,930 --> 00:02:20,510 这样我们就可以 真真正正地 28 00:02:20,510 --> 00:02:21,650 与它进行交流互动 29 00:02:21,650 --> 00:02:24,250 事实上 我下面要做的 是展示一个 30 00:02:24,250 --> 00:02:25,860 能在这个软件上运行的程序 31 00:02:25,860 --> 00:02:28,700 我把这个传感器放在我的笔记本前面 32 00:02:28,700 --> 00:02:32,420 正如你们所看到的 它意识到了我的手在这里 33 00:02:32,420 --> 00:02:33,400 控制着它 34 00:02:33,400 --> 00:02:37,900 而这个装置可以流畅地进行这样的识别 35 00:02:37,900 --> 00:02:41,080 不过你们可以看到 现在我5根手指在这儿 36 00:02:41,080 --> 00:02:44,270 如果我们放10根 你可以想像有一个钢琴 37 00:02:44,270 --> 00:02:45,390 或者其他类似的app 38 00:02:45,390 --> 00:02:47,820 所以如果你今天课后想来试用一下 39 00:02:47,820 --> 00:02:49,720 我们十分欢迎 40 00:02:49,720 --> 00:02:52,930 在今后的课上我们还会持续关注它 41 00:02:52,930 --> 00:02:54,420 一些课程通知 42 00:02:54,420 --> 00:02:58,690 一 请在周五中午前完成你们的小班课 请点击 cs50.net/section 43 00:02:58,690 --> 00:03:01,260 这个链接你们也可以在课程网站主页上找到 44 00:03:01,260 --> 00:03:04,010 同时 这周日 超级小班课会开始 45 00:03:04,010 --> 00:03:07,800 超级小班课是一个只会发生一次的课 因为我们安排 46 00:03:07,800 --> 00:03:09,470 谁在什么时间在哪上课 很费工夫 47 00:03:09,470 --> 00:03:12,280 所以这周日 会有一场课给那些不太适应这门课的人 48 00:03:12,280 --> 00:03:14,040 另一场给那些比较适应这门课的人 49 00:03:14,040 --> 00:03:16,110 而觉得自己居于两者中间的 可以随便去听任意一场 50 00:03:16,110 --> 00:03:17,850 都去或者都不去 51 00:03:17,850 --> 00:03:19,150 我们会录下两场课程 52 00:03:19,150 --> 00:03:22,740 课会在一个较大的礼堂里上 比平时的小班课教室要大些 53 00:03:22,740 --> 00:03:26,110 不过这课目的是让你们在这周末 54 00:03:26,110 --> 00:03:29,910 在你们知道自己正式的小班课分组之前 55 00:03:29,910 --> 00:03:31,050 能自如地面对练习一 C语言 及CS50工具 56 00:03:31,050 --> 00:03:34,700 所以在差不多一周内 你们就能见你自己的助教 57 00:03:34,700 --> 00:03:36,350 及小班课上的同学了 58 00:03:36,350 --> 00:03:38,200 办公室时间会正常开放 59 00:03:38,200 --> 00:03:41,020 请好好利用今晚及明晚的办公室时间 60 00:03:41,020 --> 00:03:43,460 练习0周五要交 61 00:03:43,460 --> 00:03:46,680 这比一般定的周四要晚一天 62 00:03:46,680 --> 00:03:50,410 不过对于练习1 你们会发现其中包括了一些暖身题 63 00:03:50,410 --> 00:03:52,770 所以你们可以把周四的截止日期 64 00:03:52,770 --> 00:03:54,660 延长到周五 65 00:03:54,660 --> 00:03:58,410 练习1 会在周五于课程网站上放出 66 00:03:58,410 --> 00:04:02,000 这样你们就可以像我以前一样 67 00:04:02,000 --> 00:04:04,370 在周五晚上就开始解决它了 68 00:04:04,370 --> 00:04:07,660 在cs50.net/appliance上你可以找到我们在周一就开始用的 69 00:04:07,660 --> 00:04:09,040 CS50的相关工具 70 00:04:09,040 --> 00:04:10,140 我们今天会用得更多 71 00:04:10,140 --> 00:04:14,040 不过我们保证 在练习1的说明里 72 00:04:14,040 --> 00:04:16,490 你可以找到详细的指示 教你怎样安装它 73 00:04:16,490 --> 00:04:22,120 所以如果你在练习1放出前还没有装的话 也不用担心 74 00:04:22,120 --> 00:04:22,660 好的 75 00:04:22,660 --> 00:04:27,180 我们在周一看了源代码 不过是在 76 00:04:27,180 --> 00:04:30,895 C语言的环境下看的 C语言和Scratch不同 不是图象化的 77 00:04:30,895 --> 00:04:32,270 它没有那些拼图块 78 00:04:32,270 --> 00:04:34,450 它更像是英语的句法 79 00:04:34,450 --> 00:04:37,140 在C语言中真正写出并运行一个程序之前 还有许多步要做 80 00:04:37,140 --> 00:04:41,650 因为除了这样的源代码之外 你还需要 81 00:04:41,650 --> 00:04:42,890 一个叫做编译器的东西 82 00:04:42,890 --> 00:04:46,682 用普通人的话说 编译器是干什么用的呢? 83 00:04:46,682 --> 00:04:47,650 你 84 00:04:47,650 --> 00:04:51,040 它会把你写的代码转成一堆0与1 85 00:04:51,040 --> 00:04:51,151 很好 86 00:04:51,151 --> 00:04:53,580 所以它能把我们写的代码转换为0与1 87 00:04:53,580 --> 00:04:57,730 它会把所谓的源代码转换为目标代码 而后者 88 00:04:57,730 --> 00:04:59,140 长得像这样 89 00:04:59,140 --> 00:05:02,860 你们的CPU 也就是电脑的大脑 因为造成这些电脑的人 90 00:05:02,860 --> 00:05:06,280 才懂得这些0与1 91 00:05:06,280 --> 00:05:07,460 的排列代表着什么 92 00:05:07,460 --> 00:05:08,640 也许它代表 输出 93 00:05:08,640 --> 00:05:10,265 也许它代表 添加 94 00:05:10,265 --> 00:05:11,610 也许它代表 减去 95 00:05:11,610 --> 00:05:13,350 也许它代表 展示一张图 96 00:05:13,350 --> 00:05:16,870 这些都是预先设定好的排列 我们规定它们 97 00:05:16,870 --> 00:05:17,700 代表一些特定的东西 98 00:05:17,700 --> 00:05:20,760 但是在这门课里 大多数情况下 我们都在更高层面上看问题 99 00:05:20,760 --> 00:05:24,180 所以我们就默认 这世上存在像编译器这样的东西 100 00:05:24,180 --> 00:05:27,670 它们都让我们的源代码按我们想要的方式运作 101 00:05:27,670 --> 00:05:31,660 所以我们在上周先写后运行的程序 102 00:05:31,660 --> 00:05:33,920 也就是这周一在C语言里的那个程序 是这个 103 00:05:33,920 --> 00:05:35,700 当点击绿色旗帜时 say hellp 104 00:05:35,700 --> 00:05:37,430 这个 当然 是在Scratch里写的 105 00:05:37,430 --> 00:05:40,710 而我说 与之相对应的程序在C语言里 106 00:05:40,710 --> 00:05:41,520 会长得像这样 107 00:05:41,520 --> 00:05:45,050 所以我想 我们先来分解一下 108 00:05:45,050 --> 00:05:48,790 第一眼看时 确实 它是很难懂的句法 但是你很快 109 00:05:48,790 --> 00:05:50,160 会开始发现一些规律 110 00:05:50,160 --> 00:05:53,770 同样 我们在今天要做的 是让你们对一些 111 00:05:53,770 --> 00:05:57,280 标准的编程构造有思想准备 112 00:05:57,280 --> 00:06:00,420 然后 我们就会就一些例子来进行实践 113 00:06:00,420 --> 00:06:04,140 所以对于那些对课程更得心应手的同学来说 你们会发现 114 00:06:04,140 --> 00:06:05,940 这周与下一周的内容可能对你们来说是一个复习 115 00:06:05,940 --> 00:06:08,810 但是当练习1的黑客版本于周五上线时 116 00:06:08,810 --> 00:06:12,330 我相信你们会觉得 117 00:06:12,330 --> 00:06:15,390 虽然这两周对于你们是查漏补缺 但是你们也会 118 00:06:15,390 --> 00:06:18,410 渐渐发现课程越来越有挑战性 学到的新东西越来越多 119 00:06:18,410 --> 00:06:21,310 所以 让我们把这个程序分成几行一段来看 120 00:06:21,310 --> 00:06:24,140 在最上面 我们可以看见 121 00:06:24,140 --> 00:06:25,950 一个叫做 预处理器的东西 122 00:06:25,950 --> 00:06:30,510 这个是一行代码 说要包括文件内容 123 00:06:30,510 --> 00:06:35,080 也就是标准的I/N 标准输入-- 不好意思 应该是标准的I/O.h 124 00:06:35,080 --> 00:06:38,670 也就是我程序中 标准的输入/输出 125 00:06:38,670 --> 00:06:41,670 换句话来说 如果我在一个更简单的文件处理器里写这个程序 126 00:06:41,670 --> 00:06:45,260 比如说 gedit 一个Microsoft Word的简单版 127 00:06:45,260 --> 00:06:50,970 这个指令 #include stdio.h 就会告诉编译器 128 00:06:50,970 --> 00:06:56,080 把另一个文件 stdio.h中的内容抓取出来 然后贴在这里 129 00:06:56,080 --> 00:06:57,090 那么现在 我为什么要在乎这个? 130 00:06:57,090 --> 00:07:01,850 我们说 这个储存在我硬盘某处 或者在这里 131 00:07:01,850 --> 00:07:05,490 储存在CS50工具某处的 叫stdio.h的文件里面到底有什么呢? 132 00:07:05,490 --> 00:07:06,635 有别人帮我把它放在那儿了 133 00:07:06,635 --> 00:07:09,910 但是 它里面有什么呢? 134 00:07:09,910 --> 00:07:13,020 没错 它是这个函数 printf 的声明 135 00:07:13,020 --> 00:07:17,400 printf 可以召回这个函数 136 00:07:17,400 --> 00:07:20,160 在屏幕上按我的要求显示文字和数字 137 00:07:20,160 --> 00:07:21,220 但是这不是我写的 138 00:07:21,220 --> 00:07:22,230 也不是CS50写的 139 00:07:22,230 --> 00:07:26,990 某个人很多年以前就写好它了 然后他把独家秘方保存在了 140 00:07:26,990 --> 00:07:29,110 stdio.h这个文件里 141 00:07:29,110 --> 00:07:33,240 所以这时的第一行 让我可以使用这个有人很多年前 142 00:07:33,240 --> 00:07:36,500 就先写好的东西 里面包括printf 143 00:07:36,500 --> 00:07:39,450 现在 下面这一行 我们在下周之前都不会碰 144 00:07:39,450 --> 00:07:43,620 现在 只要知道 int main (void) 就等于 145 00:07:43,620 --> 00:07:46,310 当点击绿色旗子时 这一块黄色的拼图块就行了 146 00:07:46,310 --> 00:07:49,510 许多年前 人们就决定 如果你在用C语言写程序的话 147 00:07:49,510 --> 00:07:53,150 你最一开始要写一行 148 00:07:53,150 --> 00:07:54,410 这样子的东西 149 00:07:54,410 --> 00:07:57,650 在差不多一周后 你们就会明白 int 是什么 void 是什么了 150 00:07:57,650 --> 00:08:00,840 但是现在 只要把它们想成这块黄色的拼图就行了 151 00:08:00,840 --> 00:08:04,550 下面 我们看到一对大括号 152 00:08:04,550 --> 00:08:05,240 大致是这样 153 00:08:05,240 --> 00:08:07,430 我们在C语言中会一直看见它们 154 00:08:07,430 --> 00:08:08,930 在JavaScript和PHP中也一样 155 00:08:08,930 --> 00:08:11,345 它们包覆住了相关的代码们 156 00:08:11,345 --> 00:08:14,600 左大括号表示 下面会有一些代码 157 00:08:14,600 --> 00:08:17,590 而右大括号 也就是与前者相反方向的 158 00:08:17,590 --> 00:08:19,920 表示 相关联的代码就到此为止了 159 00:08:19,920 --> 00:08:22,970 所以在第一个程序里的最有意思的是剩下的这一行代码 160 00:08:22,970 --> 00:08:25,080 printf "hello world" 161 00:08:25,080 --> 00:08:30,670 我上一次把引号中间的东西叫什么? 162 00:08:30,670 --> 00:08:31,285 是字串 163 00:08:31,285 --> 00:08:35,309 字串是个术语 表示 164 00:08:35,309 --> 00:08:37,169 一个字符 词 或词组的序列 165 00:08:37,169 --> 00:08:39,380 就算是一个字母 也可以放在引号中 166 00:08:39,380 --> 00:08:41,840 但它是由零个或多个字符组成的序列 167 00:08:41,840 --> 00:08:43,650 不过 \n 看着有点怪 168 00:08:43,650 --> 00:08:46,290 不过它对电脑来说 是个很简单的东西 169 00:08:46,290 --> 00:08:48,060 \n 代表什么呢? 170 00:08:48,060 --> 00:08:48,570 新一行 171 00:08:48,570 --> 00:08:52,490 所以 它是一种特殊排列的字符 172 00:08:52,490 --> 00:08:56,880 我们决定让它代表 换行 因为如果你 173 00:08:56,880 --> 00:09:02,280 很理所当然 但错误地 想按下回车键换行的话 174 00:09:02,280 --> 00:09:04,570 就会把编译器弄糊涂 175 00:09:04,570 --> 00:09:07,150 你得说得更明白些 所以要用\n 176 00:09:07,150 --> 00:09:10,540 我们还会发现 其他一些特殊字符的规律 177 00:09:10,540 --> 00:09:14,340 比如 如果你想让自己做点特别的 178 00:09:14,340 --> 00:09:18,080 比如说我想要在屏幕上输出双引号 179 00:09:18,080 --> 00:09:21,840 就会有点麻烦 是不是 180 00:09:21,840 --> 00:09:24,990 因为我把一个引号放在了 "hello, world" 里 181 00:09:24,990 --> 00:09:30,120 不管是出于什么原因 为什么这可能导致问题呢? 182 00:09:30,120 --> 00:09:32,180 它打断了字串 183 00:09:32,180 --> 00:09:34,700 编译器只是一个程序 184 00:09:34,700 --> 00:09:37,210 像编译器这样的程序只会把你写的代码从上往下 185 00:09:37,210 --> 00:09:37,990 从左往右读 186 00:09:37,990 --> 00:09:41,850 如果它发现有3个双引号 而不是2个的话 它并不会知道 187 00:09:41,850 --> 00:09:45,210 左边的这东西是一个字串 还是右边的 188 00:09:45,210 --> 00:09:46,570 还是整个东西都是一个字串 189 00:09:46,570 --> 00:09:50,560 太模棱两可了 所以一般的编译器就会发疯 190 00:09:50,560 --> 00:09:53,710 然后向你报错 逼你来解决这个问题 191 00:09:53,710 --> 00:09:58,120 所以 加上\n 表示开始新一行 192 00:09:58,120 --> 00:10:02,610 现在 凭直觉 你想要在这个已经包含在引号里的字串里加一个特别的东西 193 00:10:02,610 --> 00:10:06,210 比如一个双引号 你应该怎么做? 194 00:10:06,210 --> 00:10:07,640 \" 195 00:10:07,640 --> 00:10:09,630 我们还会看到这种规律 196 00:10:09,630 --> 00:10:12,490 如果你想做点奇怪的事 你会发现 197 00:10:12,490 --> 00:10:15,060 解决的办法往往都遵从某种规律 而\ 198 00:10:15,060 --> 00:10:17,150 表示一个转义序列 199 00:10:17,150 --> 00:10:20,320 这表示某种特殊的东西 我们需要 200 00:10:20,320 --> 00:10:21,060 用一个不同的方法来表示它 201 00:10:21,060 --> 00:10:23,830 至少于要怎么样表示\ 202 00:10:23,830 --> 00:10:24,550 我们以后会说到 203 00:10:24,550 --> 00:10:26,930 不过这个问题的答案 其实 也很明显 204 00:10:26,930 --> 00:10:31,080 现在 我们来介绍另一些你们在Scratch中 205 00:10:31,080 --> 00:10:31,915 看到的编程结构 206 00:10:31,915 --> 00:10:34,790 如果你们已经开始做练习0的话 207 00:10:34,790 --> 00:10:36,060 你也许已经习惯它们了 208 00:10:36,060 --> 00:10:40,950 但是现在 我们要来介绍这些本来很易懂的想法 209 00:10:40,950 --> 00:10:42,300 在C语言里是怎样的 大部分我们都会讲到 210 00:10:42,300 --> 00:10:45,570 所以这里的条件 或者分支 211 00:10:45,570 --> 00:10:49,330 我们先前是用左边这样的代码在Scratch里表示的 212 00:10:49,330 --> 00:10:52,200 如果x小于y 那么就显示这个 213 00:10:52,200 --> 00:10:56,760 不过现在在C语言中 让我举更简单些的例子 214 00:10:56,760 --> 00:11:01,235 比如说 如果 左括号 某件事是真的--我在提出条件时 215 00:11:01,235 --> 00:11:04,190 就要在这放一个布尔表达式--右括号 216 00:11:04,190 --> 00:11:08,170 那么就做大括号内的事 217 00:11:08,170 --> 00:11:11,020 所以这里 同样 大括号就有点像Scratch里的 218 00:11:11,020 --> 00:11:12,460 U字型拼图块 219 00:11:12,460 --> 00:11:14,890 做这些大括号内的事 220 00:11:14,890 --> 00:11:16,790 在这里 也就是// 221 00:11:16,790 --> 00:11:19,720 请注意 在这里 是正斜线 不是反斜线 222 00:11:19,720 --> 00:11:21,440 // 对于那些编过程的人来说 223 00:11:21,440 --> 00:11:23,370 意味着 224 00:11:23,370 --> 00:11:24,190 这只是一个评论 225 00:11:24,190 --> 00:11:26,630 评论不是一行代码 226 00:11:26,630 --> 00:11:30,200 它是一行英文 是你这个人给自己写的 227 00:11:30,200 --> 00:11:33,540 以提醒自己去做某事 或向自己或他人解释 228 00:11:33,540 --> 00:11:35,260 你的代码在做什么 229 00:11:35,260 --> 00:11:37,050 它只是一个描述性的评论 230 00:11:37,050 --> 00:11:40,880 现在 当然 我们这样做会有一个分叉路口 231 00:11:40,880 --> 00:11:43,930 我们也可以创造一个三岔路口 如果你继续下去 232 00:11:43,930 --> 00:11:47,570 你也可以有一个四岔 五岔 六岔路口 233 00:11:47,570 --> 00:11:50,150 是的 如果你想要设这么多的条件的话 234 00:11:50,150 --> 00:11:52,010 请注意这里的这种并行结构 235 00:11:52,010 --> 00:11:55,070 if (条件) else if (条件) 唯一有点奇怪的是剩下的一个 236 00:11:55,070 --> 00:11:58,010 也就是else 237 00:11:58,010 --> 00:12:01,170 不过 这从概念上来说 和我们先前所做的 238 00:12:01,170 --> 00:12:04,690 是一样的 虽然句法需要我们花点时间来适应 239 00:12:04,690 --> 00:12:07,730 现在 在这个例子里 另有些的句法 240 00:12:07,730 --> 00:12:11,220 我们又看到了printf 括号 241 00:12:11,220 --> 00:12:13,190 在里面是一个带引号的字串 242 00:12:13,190 --> 00:12:17,060 在printf里 括号中的 243 00:12:17,060 --> 00:12:18,160 是什么东西 244 00:12:18,160 --> 00:12:22,190 我们把这个引号中的字串统称为什么? 245 00:12:22,190 --> 00:12:23,320 是参数 246 00:12:23,320 --> 00:12:26,620 任何时候 如果你有一个函数 比如说 printf 247 00:12:26,620 --> 00:12:30,330 以及括号包住的某种东西 不论是字串 248 00:12:30,330 --> 00:12:34,420 还是整数 或者是其他的什么 这个在括号中的东西 249 00:12:34,420 --> 00:12:35,110 就叫做参数 250 00:12:35,110 --> 00:12:37,910 而参数能影响函数行为特征 251 00:12:37,910 --> 00:12:39,990 在这里 很明显 252 00:12:39,990 --> 00:12:44,480 x 00:12:47,720 而printf会把它输出 254 00:12:47,720 --> 00:12:51,590 因为很显然 那个很多年前设计出printf的人 255 00:12:51,590 --> 00:12:55,000 没有想到后人会用printf做什么 256 00:12:55,000 --> 00:12:58,610 所以才有参数存在 这样它就能改变已经写好的函数 257 00:12:58,610 --> 00:12:59,450 的行为特征了 258 00:12:59,450 --> 00:13:00,780 好的 布尔表达式 259 00:13:00,780 --> 00:13:02,470 我们在Scratch中看到过它 260 00:13:02,470 --> 00:13:06,680 而在C语言中 我们发现 你也可以用"或者"把它们联在一起 261 00:13:06,680 --> 00:13:10,930 两条竖线表示这一段代码 会运行 262 00:13:10,930 --> 00:13:15,350 如果第一个条件为真 或第二个条件为真的话 263 00:13:15,350 --> 00:13:17,710 虽然 你可以没在Scratch里做过这个 264 00:13:17,710 --> 00:13:20,580 你是可以在Scratch里完成它的 而且 你还可以用不同方式表达自己 -- 265 00:13:20,580 --> 00:13:25,300 如果第一个和第二个条件为真 266 00:13:25,300 --> 00:13:27,160 那么就执行在大括号中的事 267 00:13:27,160 --> 00:13:31,210 补充一句 它之所以是有两个 & 268 00:13:31,210 --> 00:13:34,610 和两个| 是因为 一个 | 和一个& 269 00:13:34,610 --> 00:13:38,710 实际上在C语言中有不同含义 所以现在 270 00:13:38,710 --> 00:13:41,840 符号出现两遍是我们有意为之的 271 00:13:41,840 --> 00:13:45,070 那么 让我们简单介绍一下其余的句法 272 00:13:45,070 --> 00:13:48,940 这和Scratch中好像没有对应 但是 273 00:13:48,940 --> 00:13:55,190 我可以用一个 if, else if, else if, else if, else语句来完成这件事 274 00:13:55,190 --> 00:13:56,760 而这个语言也就是交换语句 275 00:13:56,760 --> 00:14:00,820 而它存在的意义就是给你 也就是编程者 276 00:14:00,820 --> 00:14:05,470 提供一种不同的解决问题的方法 虽然逻辑上来说 277 00:14:05,470 --> 00:14:07,340 它并没有给你任何新的能力 278 00:14:07,340 --> 00:14:08,530 我说这个的意思 279 00:14:08,530 --> 00:14:13,330 是当你说 switch (交换) 空格 然后一对括号 280 00:14:13,330 --> 00:14:14,570 里面放着一个表达式 281 00:14:14,570 --> 00:14:18,010 这个一开始可能看着不是很明显 但严格意义上来说 282 00:14:18,010 --> 00:14:20,680 它不是一个参数 因为 switch 不是一个函数 283 00:14:20,680 --> 00:14:24,230 所以现在 就认为我们是在不同情境下 284 00:14:24,230 --> 00:14:25,250 不同理由下用括号好了 285 00:14:25,250 --> 00:14:29,310 所以 根据表达式交换 表示 我可以在这些括号中 286 00:14:29,310 --> 00:14:31,350 也就是表达式里 放一个变量 287 00:14:31,350 --> 00:14:33,090 如果这个变量 288 00:14:33,090 --> 00:14:35,400 假如说它叫x 然后它是一个整数 289 00:14:35,400 --> 00:14:38,900 那我就可以在我的这页幻灯片上计算这样一个东西 290 00:14:38,900 --> 00:14:41,690 如果x是我的变量 而我想说 291 00:14:41,690 --> 00:14:43,480 如果x=1 那么我就可以这样做 292 00:14:43,480 --> 00:14:46,660 而如果x=2 那么我就可以那样做 293 00:14:46,660 --> 00:14:50,390 否则 如果我想做另外一件不同的事 294 00:14:50,390 --> 00:14:52,750 那我就有一个默认的情况 让我去做那件事 295 00:14:52,750 --> 00:14:58,730 所以 这就等同于一个 if x==1 , else if, else语句 296 00:14:58,730 --> 00:15:01,150 我在这里提到它 是因为我们后面还会看到 297 00:15:01,150 --> 00:15:03,720 不过现在 只用知道它存在就行了 298 00:15:03,720 --> 00:15:04,220 好的 299 00:15:04,220 --> 00:15:07,660 最后这一些一开始看上去会有点复杂 300 00:15:07,660 --> 00:15:09,385 但是它们做的事很明了 301 00:15:09,385 --> 00:15:14,410 C语言中的For循环 是一堆代码 302 00:15:14,410 --> 00:15:15,330 一直重复做某件事 303 00:15:15,330 --> 00:15:17,590 唯一让我们不高兴的 是想要在循环中表达自己 304 00:15:17,590 --> 00:15:21,380 有点难 不过表达方法 305 00:15:21,380 --> 00:15:22,450 是全球通用的 306 00:15:22,450 --> 00:15:25,760 当你用一个for循环的时候 你又得用括号了 307 00:15:25,760 --> 00:15:27,570 而且注意 这里有两个分号 308 00:15:27,570 --> 00:15:34,380 这两个分号隔开了括号中 309 00:15:34,380 --> 00:15:35,020 三个不同的表达式 310 00:15:35,020 --> 00:15:37,170 一个是所谓的初始化 311 00:15:37,170 --> 00:15:38,830 一个是所谓的条件 312 00:15:38,830 --> 00:15:40,210 最后一个是所谓的更新 313 00:15:40,210 --> 00:15:43,240 抽象来看 这个很难理解 314 00:15:43,240 --> 00:15:44,630 所以让我们来看一个具体的例子 315 00:15:44,630 --> 00:15:46,720 在Scratch中我们有一个重复的块 316 00:15:46,720 --> 00:15:50,670 这个拼图块表示 重复10次 输出 "hello, world" 让我 317 00:15:50,670 --> 00:15:53,810 现在先提它一下 我们以后还会再讲到它 而你们也会 318 00:15:53,810 --> 00:15:57,345 越来越熟悉它 在C语言中与之等同的代码 319 00:15:57,345 --> 00:16:02,320 可以是这个 -- 一个for语句 空格 一对括号 然后请注意 320 00:16:02,320 --> 00:16:05,730 其中的分号分隔开了三样东西 初始化 321 00:16:05,730 --> 00:16:07,320 条件 和更新 322 00:16:07,320 --> 00:16:08,840 猜一猜第一个是做什么的 323 00:16:08,840 --> 00:16:10,690 Int i = 0 324 00:16:10,690 --> 00:16:15,120 用一般人的话来说 这个大概是干什么的? 325 00:16:15,120 --> 00:16:15,590 没错 326 00:16:15,590 --> 00:16:19,630 它是在声明 有个变量 i 而我们给了它什么值? 327 00:16:19,630 --> 00:16:20,220 0 328 00:16:20,220 --> 00:16:24,280 所以 它创造了一个变量 i 在其中存储了0这个值 329 00:16:24,280 --> 00:16:26,420 这就叫做初始化 330 00:16:26,420 --> 00:16:29,360 好的 在中间的 331 00:16:29,360 --> 00:16:31,760 i < 10 就是条件 332 00:16:31,760 --> 00:16:32,730 它是做什么的呢? 333 00:16:32,730 --> 00:16:36,560 一个带for循环的程序运行时 for循环所做的 334 00:16:36,560 --> 00:16:41,050 就是电脑每跑过这个循环一次 从上至下 335 00:16:41,050 --> 00:16:43,740 就像你们站起来数数再坐下那样 336 00:16:43,740 --> 00:16:47,090 一遍又一遍 电脑会检查 337 00:16:47,090 --> 00:16:48,560 这个条件 338 00:16:48,560 --> 00:16:51,140 如果i确实小于10 它就会再重复一次 339 00:16:51,140 --> 00:16:53,100 如果i还小于10 它就会再重复一次 340 00:16:53,100 --> 00:16:55,030 如果i还小于10 它就会再重复一次 341 00:16:55,030 --> 00:16:57,330 这好像表示 理想情况下 i在不断变化 342 00:16:57,330 --> 00:16:59,010 否则 我们就会有一个无限循环了 343 00:16:59,010 --> 00:17:02,590 确实 i是在变化 因为最后一个在分号后的东西 344 00:17:02,590 --> 00:17:05,569 看上去很难懂 是i++ 345 00:17:05,569 --> 00:17:07,630 不过 对于你们中写过这个的人来说 346 00:17:07,630 --> 00:17:08,609 这个代表着什么? 347 00:17:08,609 --> 00:17:09,730 [学生回答] 348 00:17:09,730 --> 00:17:10,740 给i加1 349 00:17:10,740 --> 00:17:11,819 增加1 350 00:17:11,819 --> 00:17:13,910 实际上 我们看到过这个的Scratch拼图块 351 00:17:13,910 --> 00:17:18,230 它看上去不像i++ 不过它表示 352 00:17:18,230 --> 00:17:24,040 每一次 给i增加1 353 00:17:24,040 --> 00:17:26,910 所以 一开始 你给i的值是0 354 00:17:26,910 --> 00:17:29,520 然后你就检查你的条件 355 00:17:29,520 --> 00:17:31,070 0与10小么? 356 00:17:31,070 --> 00:17:31,730 是的 357 00:17:31,730 --> 00:17:32,910 我们再经过循环一次 358 00:17:32,910 --> 00:17:35,150 而电脑要做的下一件事 359 00:17:35,150 --> 00:17:35,910 是给i加1 360 00:17:35,910 --> 00:17:37,080 所以现在i是1 361 00:17:37,080 --> 00:17:37,940 它会检查条件 362 00:17:37,940 --> 00:17:39,290 1比10小么? 363 00:17:39,290 --> 00:17:39,930 当然 364 00:17:39,930 --> 00:17:41,030 所以它就再重复一次 365 00:17:41,030 --> 00:17:43,580 于是它把i++加到了2 366 00:17:43,580 --> 00:17:44,610 2比10小么? 367 00:17:44,610 --> 00:17:45,230 是的 368 00:17:45,230 --> 00:17:46,670 然后重复再重复 369 00:17:46,670 --> 00:17:50,070 直到加呀加 我们得到了i等于10 370 00:17:50,070 --> 00:17:51,675 10比10小么? 371 00:17:51,675 --> 00:17:52,990 当然不对 372 00:17:52,990 --> 00:17:55,320 这一刻 这个循环停止了 373 00:17:55,320 --> 00:17:58,320 然后你后面还有别的代码 374 00:17:58,320 --> 00:18:01,620 那么电脑就会接着运行那些代码了 375 00:18:01,620 --> 00:18:05,380 所以又一次 虽然一开始 对于没有编过程的人来说 376 00:18:05,380 --> 00:18:07,830 这看上去有点奇怪 377 00:18:07,830 --> 00:18:11,640 它其实就是把Scratch里简单明了的拼图块 378 00:18:11,640 --> 00:18:14,330 抽象转换了而已 379 00:18:14,330 --> 00:18:19,130 好的 我说了还会有另一个与Scratch中类似的东西 380 00:18:19,130 --> 00:18:20,060 这里就是一个 381 00:18:20,060 --> 00:18:21,700 我们上一次也短暂地看过 382 00:18:21,700 --> 00:18:23,530 还记得无限循环块在Scratch里是什么? 383 00:18:23,530 --> 00:18:25,490 是无限次地做某件事么? 384 00:18:25,490 --> 00:18:27,470 我保证你这样也可以做到 385 00:18:27,470 --> 00:18:29,740 实际上 你有很多种方法都可以做到 386 00:18:29,740 --> 00:18:34,260 而while循环只是在C语言中 一个不同的 表达自己的方式 387 00:18:34,260 --> 00:18:37,080 到头来 你用for循环能做到的 388 00:18:37,080 --> 00:18:38,360 while循环也能做到 389 00:18:38,360 --> 00:18:41,430 所以 它们在功能上实际上是一样的 390 00:18:41,430 --> 00:18:43,840 不过它们可以让你用不同方式表达自己 391 00:18:43,840 --> 00:18:44,850 像下面这样 392 00:18:44,850 --> 00:18:49,720 while循环 是括号中的东西被 393 00:18:49,720 --> 00:18:51,050 一次又一次检查 394 00:18:51,050 --> 00:18:55,100 如果表达式为假 那么循环就停止了 395 00:18:55,100 --> 00:18:57,890 电脑会运行文件里 396 00:18:57,890 --> 00:18:59,230 你写在后面的代码 397 00:18:59,230 --> 00:19:02,180 在这里有意思的是 我真的输入了 true 398 00:19:02,180 --> 00:19:06,680 而true是布尔值 也就是 真 或 假 中的一个 399 00:19:06,680 --> 00:19:09,750 所以 true有没有可能变假呢 400 00:19:09,750 --> 00:19:11,970 如果我就在我们程序里硬编码的话? 401 00:19:11,970 --> 00:19:12,470 不会 402 00:19:12,470 --> 00:19:13,730 我这样做有点奇怪 403 00:19:13,730 --> 00:19:15,190 但是true 就是真 404 00:19:15,190 --> 00:19:16,320 没有什么++ 的改动 405 00:19:16,320 --> 00:19:17,820 这里没有变量 406 00:19:17,820 --> 00:19:22,740 因为我硬编码 while true 这个循环会一直评估 407 00:19:22,740 --> 00:19:24,090 一遍又一遍 408 00:19:24,090 --> 00:19:27,660 那么hello world会在屏幕上出现多少次? 409 00:19:27,660 --> 00:19:28,170 无限次 410 00:19:28,170 --> 00:19:31,980 直到电脑没电了 411 00:19:31,980 --> 00:19:32,730 或者其他什么事发生 412 00:19:32,730 --> 00:19:35,880 所以这不是最好的程序 因为如果用户无法 413 00:19:35,880 --> 00:19:38,660 退出你的程序 这大概不会是你想要的 414 00:19:38,660 --> 00:19:41,070 不过有时 程序应该有个无限循环 415 00:19:41,070 --> 00:19:44,050 比如 你的电脑里有时钟 416 00:19:44,050 --> 00:19:48,130 如果它能一直自己更新的话 当然比较好 417 00:19:48,130 --> 00:19:50,500 虽然是每一秒 或者每分钟更新 418 00:19:50,500 --> 00:19:53,690 所以无限循环也是有它存在的意义的 419 00:19:53,690 --> 00:19:54,360 好的 420 00:19:54,360 --> 00:19:55,190 最后 这后 421 00:19:55,190 --> 00:19:57,770 这一个从功能上来说略有不同 422 00:19:57,770 --> 00:19:59,460 在练习1中我们可能还会讲到它 423 00:19:59,460 --> 00:20:02,370 但有另一种循环 叫 do while循环 424 00:20:02,370 --> 00:20:07,100 这个循环与while循环的唯一区别 425 00:20:07,100 --> 00:20:11,120 是 do while循环当中的条件 只有在你执行代码后 426 00:20:11,120 --> 00:20:12,080 才会被检查 427 00:20:12,080 --> 00:20:15,380 所以 对于while循环来说是从上开始的 428 00:20:15,380 --> 00:20:16,560 而对于do while循环来说 是从底端开始的 429 00:20:16,560 --> 00:20:21,370 这样的话do while循环一般会比while循环[英文漏字] 430 00:20:21,370 --> 00:20:24,630 多运行还是少运行几次呢? 431 00:20:24,630 --> 00:20:30,810 应该是多运行 因为一个do while循环中 432 00:20:30,810 --> 00:20:34,600 是只有在你检查了括号内的条件是否为真后 433 00:20:34,600 --> 00:20:35,980 再执行 434 00:20:35,980 --> 00:20:37,940 我们会在练习1中再次看到这个 435 00:20:37,940 --> 00:20:41,150 如果你想至少做某事一次 然后以后再做几次的话 436 00:20:41,150 --> 00:20:43,350 用这个结构会比较好 437 00:20:43,350 --> 00:20:46,970 但是一个while循环 相反 会先检查条件 438 00:20:46,970 --> 00:20:50,660 那么C语言中的循环就讲到这里 我们讲了do while, while和for循环 439 00:20:50,660 --> 00:20:54,700 另外 它们也有Scratch对应得很好 除了 440 00:20:54,700 --> 00:20:57,350 这个以外 在Scratch中都有对应 441 00:20:57,350 --> 00:20:58,880 那么 现在来看看变量吧? 442 00:20:58,880 --> 00:21:02,600 我上一次是这样用句法来表示变量的 443 00:21:02,600 --> 00:21:06,440 我用了 int 我说它表示一个整数 444 00:21:06,440 --> 00:21:09,690 然后我放了一个变量 叫counter 然后是分号 445 00:21:09,690 --> 00:21:11,070 所以这一行代码在做什么呢? 446 00:21:11,070 --> 00:21:14,540 很简单 它在声明变量 447 00:21:14,540 --> 00:21:17,940 也就是告诉电脑 给我一些空间 一些比特 448 00:21:17,940 --> 00:21:19,450 来让我储存什么呢? 449 00:21:19,450 --> 00:21:20,300 一个int 450 00:21:20,300 --> 00:21:23,570 而这个分号只是表示 这一行代码结束了 451 00:21:23,570 --> 00:21:25,500 现在 第二行就应该很容易猜了 452 00:21:25,500 --> 00:21:29,710 counter = 0; 是在干什么? 453 00:21:29,710 --> 00:21:31,690 是把0赋予counter 454 00:21:31,690 --> 00:21:33,470 这个是 和 呃 代数相比 455 00:21:33,470 --> 00:21:34,540 又一件有点烦人的事 456 00:21:34,540 --> 00:21:39,110 在代数中 等于号表示等于 而在C语言里 457 00:21:39,110 --> 00:21:40,470 等于号表示赋值 458 00:21:40,470 --> 00:21:45,380 所以它表示 把等号右边的东西放到左边去 459 00:21:45,380 --> 00:21:49,030 我们会看见另一个符号 == 这是我们想要 460 00:21:49,030 --> 00:21:50,570 看两者是不是相同时用的 这有点烦人 461 00:21:50,570 --> 00:21:52,590 但是这个的效率有低 462 00:21:52,590 --> 00:21:55,090 我必须声明变量 463 00:21:55,090 --> 00:21:56,110 然后再给它值 这个有点烦 464 00:21:56,110 --> 00:21:59,380 所以C语言允许我们简化这一步骤 465 00:21:59,380 --> 00:22:01,330 你可以在左边声明变量 466 00:22:01,330 --> 00:22:04,590 然后在右边赋值 只要用等号 467 00:22:04,590 --> 00:22:05,030 在中间连接就行 468 00:22:05,030 --> 00:22:08,740 所以其实 这两者是一样的 但是说实话 469 00:22:08,740 --> 00:22:11,840 习惯用这一个还是比较好 因为它 470 00:22:11,840 --> 00:22:15,040 代码少 易读 而且 471 00:22:15,040 --> 00:22:17,470 让你的代码更紧凑 472 00:22:17,470 --> 00:22:22,120 现在 对于循环 变量 条件 473 00:22:22,120 --> 00:22:27,001 布尔表达式 不管从技术上还是概念上 有什么问题么? 474 00:22:27,001 --> 00:22:28,010 好的 475 00:22:28,010 --> 00:22:30,690 下面这一个要更有意思些 476 00:22:30,690 --> 00:22:34,790 这是我上次用的例子 477 00:22:34,790 --> 00:22:35,820 将Scratch与C语言相对应 478 00:22:35,820 --> 00:22:36,580 一个函数 479 00:22:36,580 --> 00:22:38,110 用普通人的话来说 函数是什么? 480 00:22:42,900 --> 00:22:44,350 回答问题的时候要大胆些 481 00:22:44,350 --> 00:22:45,020 函数是什么? 482 00:22:45,020 --> 00:22:46,320 它会做某事 483 00:22:46,320 --> 00:22:46,780 什么? 484 00:22:46,780 --> 00:22:48,000 它会做某事 485 00:22:48,000 --> 00:22:48,710 做某事 486 00:22:48,710 --> 00:22:49,000 好的 487 00:22:49,000 --> 00:22:49,590 我们就从这入手 488 00:22:49,590 --> 00:22:51,270 所以函数会做某事 489 00:22:51,270 --> 00:22:55,160 所以它是一些代码 最终会被在某处运行 490 00:22:55,160 --> 00:22:56,620 然后能做某事 491 00:22:56,620 --> 00:23:00,180 那么 我们对函数感兴趣 是因为它可以接受input 492 00:23:00,180 --> 00:23:02,710 然后产生output 493 00:23:02,710 --> 00:23:05,090 让我们来看一下这个 494 00:23:05,090 --> 00:23:09,030 我们背后团队的成员前一秒刚出现了一下 495 00:23:09,030 --> 00:23:10,320 这里我们有个桌子 496 00:23:10,320 --> 00:23:13,010 但假设它代表一个大盒子 497 00:23:13,010 --> 00:23:14,940 所以这是所谓的黑盒 498 00:23:14,940 --> 00:23:19,800 一般来说 在设计 在计算机科学里 黑盒指的是 499 00:23:19,800 --> 00:23:22,510 某人设计的一些功能 500 00:23:22,510 --> 00:23:26,140 而你并不知道或在乎 这是怎么设计出来的 501 00:23:26,140 --> 00:23:28,960 你只关心 这个黑盒 我们现在叫它函数 502 00:23:28,960 --> 00:23:30,730 它会做某事 503 00:23:30,730 --> 00:23:34,710 例如 如果台子上的这个黑盒代表printf 504 00:23:34,710 --> 00:23:38,040 一个函数 从前面的例子 我知道printf 505 00:23:38,040 --> 00:23:40,910 接受一个或多个参数 而第一个参数应该是一个字串 506 00:23:40,910 --> 00:23:44,780 像是"hello, world" 如果我是在编程的那个人 507 00:23:44,780 --> 00:23:50,460 而我想用printf 我可能会 像这样 拿一张白纸 508 00:23:50,460 --> 00:23:55,060 然后在上面用黑笔写字 509 00:23:55,060 --> 00:23:56,580 我把world 拼错了 510 00:23:56,580 --> 00:23:59,560 H-E-L-L-O 511 00:23:59,560 --> 00:24:04,220 所以我拿着黑笔 用最大最清晰的字 512 00:24:04,220 --> 00:24:07,260 在纸上写下 "hello, world," 我说这就是我的参数 513 00:24:07,260 --> 00:24:09,740 这是一个用一张白纸表示的字串 514 00:24:09,740 --> 00:24:13,030 而现在函数printf的input就会是这个 515 00:24:13,030 --> 00:24:18,250 所以我召出pirnft 将参数变为它的input 516 00:24:22,110 --> 00:24:26,740 现在 我不知道很多年前编写出printf的那个人是怎样 517 00:24:26,740 --> 00:24:31,110 但是我知道 根据记录 它存在的目的就是把我提供的input 518 00:24:31,110 --> 00:24:34,410 输出出来 519 00:24:34,410 --> 00:24:40,630 所以 虽然它的背后设计我并不知道 520 00:24:40,630 --> 00:24:44,170 现在我可以看到 成功了 521 00:24:44,170 --> 00:24:45,740 它在屏幕上输出了点东西 522 00:24:45,740 --> 00:24:49,070 而现在 程序的控制权 如果有多行代码的话 523 00:24:49,070 --> 00:24:51,070 就会回到我手上 524 00:24:51,070 --> 00:24:52,290 所以虽然Colton 525 00:24:52,290 --> 00:24:55,370 保佑他 正躲在你们面前的桌子下 526 00:24:55,370 --> 00:24:59,530 就表示 我并不知道 也并不关心 527 00:24:59,530 --> 00:25:00,100 printf是怎样实现的 528 00:25:00,100 --> 00:25:03,390 我只知道 根据记录 它能做什么 529 00:25:03,390 --> 00:25:05,040 以及我该怎样用它 530 00:25:05,040 --> 00:25:09,140 现在 记得printf可以变得更复杂些 531 00:25:09,140 --> 00:25:12,220 我们是在讲和Scratch中的Say拼图块相同的 532 00:25:12,220 --> 00:25:14,230 上一次我也这么做了 533 00:25:14,230 --> 00:25:17,270 我想要让我的hello程序 更多元些 534 00:25:17,270 --> 00:25:19,740 而不是仅仅是个在程序中 加入"world"这样的硬编码 535 00:25:19,740 --> 00:25:22,520 更不是D-A-V-I-D这样主观的东西 536 00:25:22,520 --> 00:25:27,510 我想要问用户要他或她的名字 537 00:25:27,510 --> 00:25:29,720 然后用他们提供的字串做点什么 538 00:25:29,720 --> 00:25:32,690 所以现在 和刚才的printf 略有不同 539 00:25:32,690 --> 00:25:35,860 刚才的printf 确实 做了某事 但它并没有把任何东西返回给我 540 00:25:35,860 --> 00:25:36,020 对不对? 541 00:25:36,020 --> 00:25:38,910 Colton没有递给我任何东西 一张纸也没有 542 00:25:38,910 --> 00:25:40,320 发生的只是一个副作用 543 00:25:40,320 --> 00:25:44,510 我把 "hello, world" 当作参数给Colton 544 00:25:44,510 --> 00:25:48,420 结果是副作用得到的一个词 一些词 出现在了屏幕上 545 00:25:48,420 --> 00:25:51,350 不过 getstring 就不一样了 getstring还是 546 00:25:51,350 --> 00:25:54,590 一个函数 但是它能返回一些值 547 00:25:54,590 --> 00:25:56,370 它不是只有一个副作用 548 00:25:56,370 --> 00:26:00,230 它是真的能给我 那个在使用这个函数的人 549 00:26:00,230 --> 00:26:01,320 一些东西 550 00:26:01,320 --> 00:26:05,740 所以在这里要用 getstring 我们这儿写了 551 00:26:05,740 --> 00:26:07,510 GetString () 552 00:26:07,510 --> 00:26:11,370 那么getstring 有接受任何参数或input么 553 00:26:11,370 --> 00:26:12,340 不 看起来没有 554 00:26:12,340 --> 00:26:14,460 它存在的意思是 获取一个字串 555 00:26:14,460 --> 00:26:16,910 其他的就都不需要 556 00:26:16,910 --> 00:26:20,430 那就让我再一次 假设这个黑盒不是printf 557 00:26:20,430 --> 00:26:25,160 而是getstring 并且让我 在编程的这个人 召出或者使用 558 00:26:25,160 --> 00:26:29,720 getstring 也就是写下 G-E-T-S-T-R-I-N-G () 559 00:26:29,720 --> 00:26:32,170 获取字串 560 00:26:32,170 --> 00:26:36,920 我现在一点也不知道CS50的工作人员怎么设计出getstring的 但我知道 561 00:26:36,920 --> 00:26:41,240 如果我等得够久 它就会做它该做的事 562 00:26:41,240 --> 00:26:44,940 也许是用了变量 也许是用了条件 也许是用了循环 563 00:26:44,940 --> 00:26:48,170 也许是用了函数 也许是用了 -- 564 00:26:48,170 --> 00:26:52,290 我只是在拖延时间 -- 也许用是其他的编程功能 565 00:26:52,290 --> 00:26:55,350 但只要我等得够久 在现实中 电脑里 566 00:26:55,350 --> 00:26:56,270 这个其实速度很快 567 00:26:56,270 --> 00:26:59,910 如果我等得够久 getstring这个函数就会从用户手中 568 00:26:59,910 --> 00:27:04,060 获取一个字串 这应该是用户用键盘输入的 569 00:27:04,060 --> 00:27:08,090 然后 当getstring从用户手中 获取完这些字符 570 00:27:08,090 --> 00:27:14,080 并把它们储存好之后 getstring这个函数 571 00:27:14,080 --> 00:27:17,990 就会给我准备好output 我可以通过 572 00:27:17,990 --> 00:27:19,470 赋值运算符得到这个output 573 00:27:19,470 --> 00:27:25,390 如果我看一下这个输出 Obosi 574 00:27:25,390 --> 00:27:29,900 在不知情的情况下 倾情参与 在变量里写下了 575 00:27:29,900 --> 00:27:33,100 他的名字 代表了一个字串 576 00:27:33,100 --> 00:27:35,640 赋值运算符表示 虽然听起来有点累赘 577 00:27:35,640 --> 00:27:38,790 实际上 我确实需要自己来复制一份这个 578 00:27:38,790 --> 00:27:40,700 因为在左边 哎哟 579 00:27:40,700 --> 00:27:45,760 注意 在左手边我有 字串名字 580 00:27:45,760 --> 00:27:48,280 所以我要自己来复制一份 581 00:27:48,280 --> 00:27:50,990 其实这是个善意的谎言 因为在一两周内 582 00:27:50,990 --> 00:27:54,100 我们就会发现字串其实并不是它们看起来这个样子 583 00:27:54,100 --> 00:27:56,700 但现在 这是返回来的值 584 00:27:56,700 --> 00:28:00,160 这是我在用了赋值运算符后 自己得到的一份 585 00:28:00,160 --> 00:28:01,790 那么下面我该怎么做? 586 00:28:01,790 --> 00:28:04,080 我们看到了两行代码中的第二行 587 00:28:04,080 --> 00:28:05,640 所以我现在想要用printf 588 00:28:05,640 --> 00:28:08,500 让我们再一次假装这个黑盒子是printf 589 00:28:08,500 --> 00:28:10,210 而不是getstring 590 00:28:10,210 --> 00:28:12,200 这一次printf有多少个参数? 591 00:28:14,890 --> 00:28:16,770 看上去是2个 592 00:28:16,770 --> 00:28:18,860 这里有2个逗号 但是其中的一个逗号 593 00:28:18,860 --> 00:28:20,220 是在引号里面的 594 00:28:20,220 --> 00:28:23,190 所以第一个参数其实就是这个 595 00:28:23,190 --> 00:28:31,850 H-E-L-L-O 逗号 %s \n 596 00:28:31,850 --> 00:28:36,560 现在我向printf提供了 不是一个 而是两个参数 597 00:28:36,560 --> 00:28:39,530 那么printf 在我把这些input当作参数给它后 598 00:28:39,530 --> 00:28:42,050 会怎么做呢? 599 00:28:42,050 --> 00:28:45,360 它应该要接受第二个 也就是name 600 00:28:45,360 --> 00:28:48,660 所以我刚才写的第二张纸叫name 601 00:28:48,660 --> 00:28:53,550 它会把变量中的值放到占位符%s里去 602 00:28:53,550 --> 00:28:58,310 所以 又一次 我们马上会看到 603 00:28:58,310 --> 00:29:04,180 用printf的副作用 也就是我们现在看到的 不是 "hello, world," 604 00:29:04,180 --> 00:29:04,710 而是 "hello, Obosi." 605 00:29:04,710 --> 00:29:08,730 请掌声感谢我们的志愿者 606 00:29:08,730 --> 00:29:12,010 他们中只有一个预先知道这些 607 00:29:12,010 --> 00:29:12,990 好的 608 00:29:12,990 --> 00:29:16,480 也许这看上去很简单 609 00:29:16,480 --> 00:29:20,190 如果你已经比较熟悉这些的话 但 希望你会永远记得 610 00:29:20,190 --> 00:29:21,220 函数运作时是什么样子 611 00:29:21,220 --> 00:29:23,000 所以 我们不只有getstring 612 00:29:23,000 --> 00:29:26,020 在CS50库里 还有一堆函数 613 00:29:26,020 --> 00:29:29,510 都用大写标出 表示是我们写的 614 00:29:29,510 --> 00:29:33,610 一般 在C语言中 你们用的其他函数都是小写的 615 00:29:33,610 --> 00:29:36,190 但我们故意用了大写字母 表示这些只是 616 00:29:36,190 --> 00:29:39,880 在这一两周用来给你们练习的 617 00:29:39,880 --> 00:29:42,890 它们简化了从用户那儿得到input的过程 618 00:29:42,890 --> 00:29:46,740 CS50库里 并没有什么 619 00:29:46,740 --> 00:29:50,230 是你用C语言 照着教科书写不出来的 620 00:29:50,230 --> 00:29:53,310 但是 再一次 我们用这些只是为了让你们在这一两周练习 621 00:29:53,310 --> 00:29:57,250 这样我们就可以抛去复杂性 让你们懂得 622 00:29:57,250 --> 00:30:00,460 自己其实在做的 就是简单地 623 00:30:00,460 --> 00:30:02,080 从用户手中拿input 624 00:30:02,080 --> 00:30:05,540 所以你们也可以用 GetChar GetDouble 625 00:30:05,540 --> 00:30:06,050 双精度浮点数 626 00:30:06,050 --> 00:30:07,050 然后还有GetFloat 627 00:30:07,050 --> 00:30:07,600 什么是浮点数? 628 00:30:07,600 --> 00:30:09,382 我们就从这开始 629 00:30:09,382 --> 00:30:10,600 [学生回答] 630 00:30:10,600 --> 00:30:10,790 没错 631 00:30:10,790 --> 00:30:12,120 它是一个数 带着小数点的数 632 00:30:12,120 --> 00:30:15,930 所以int表示整数 就是0到9之间的数字 633 00:30:15,930 --> 00:30:18,940 的重复 而float(浮点数) 634 00:30:18,940 --> 00:30:20,100 是有小数点的数 635 00:30:20,100 --> 00:30:26,090 一个双精度浮点数呢 也带小数点 636 00:30:26,090 --> 00:30:28,160 但是小数点后的数字会更多 637 00:30:28,160 --> 00:30:30,040 我们过一会会再讲到这个 638 00:30:30,040 --> 00:30:34,560 但一般来说 我们的库可以返回给你们的 639 00:30:34,560 --> 00:30:39,380 像这样的每一种数据类型 变量类型 640 00:30:39,380 --> 00:30:40,290 都用不同长度的比特来储存的 641 00:30:40,290 --> 00:30:43,910 一般 一个Char 它就表示一个字符 用8位 642 00:30:43,910 --> 00:30:47,490 这实际上和我们上周让志愿者 643 00:30:47,490 --> 00:30:50,410 上台来表现Ascii 字符的时候 是符合的 644 00:30:50,410 --> 00:30:51,850 所以一个Char有8位 645 00:30:51,850 --> 00:30:54,430 一个浮点数呢 一般有32位 646 00:30:54,430 --> 00:30:59,230 而一个双精度浮点数 你们大概猜到了 有64位 也就是更多的数字 647 00:30:59,230 --> 00:31:01,360 这也就是说你可以表示更大的数 或更精确地表示数字 648 00:31:01,360 --> 00:31:03,000 不过 我们后面还会再讲到它 649 00:31:03,000 --> 00:31:06,550 GetLongLong 现在看上去有个很蠢的名字 650 00:31:06,550 --> 00:31:10,770 只是一个比一般整数大一倍 长一倍的整数 651 00:31:10,770 --> 00:31:12,940 它有64位 而不是32位 652 00:31:12,940 --> 00:31:14,560 而GetString 我们已经一直在用了 653 00:31:14,560 --> 00:31:18,870 不过我们发现 在CS50库里 654 00:31:18,870 --> 00:31:23,560 它们是以两个文件的形式存在的 一个叫cs50.h 655 00:31:23,560 --> 00:31:24,770 这里面还有另外两种数据类型 656 00:31:24,770 --> 00:31:29,700 布尔表达式在C语言中不存在 你可以在程序中 657 00:31:29,700 --> 00:31:30,850 用0与1们来模拟它 658 00:31:30,850 --> 00:31:35,500 但我们在CS50库里创造了"true"与"false"符号 659 00:31:35,500 --> 00:31:38,580 来表示1与0 这样你们就不用自己 660 00:31:38,580 --> 00:31:39,810 来用1与0来硬编码了 661 00:31:39,810 --> 00:31:40,980 不过我们在后面还会看到它们 662 00:31:40,980 --> 00:31:42,330 字串 一样 也不存在 663 00:31:42,330 --> 00:31:44,520 这也是为什么我说 这现在是一个善意的谎言 664 00:31:44,520 --> 00:31:46,660 不过我们不久就会来揭开它的真面目 665 00:31:46,660 --> 00:31:49,540 不过现在 一个字串是一些字符组成的序列 666 00:31:49,540 --> 00:31:51,790 在C语言中 我们确实有不同的数据类型 667 00:31:51,790 --> 00:31:53,200 注意 这些都是小写的 668 00:31:53,200 --> 00:31:56,960 所以你们刚才看到的函数都是CS50写好的 669 00:31:56,960 --> 00:32:02,310 会把值返回给你 而这个值属于这其中的某个类型 670 00:32:02,310 --> 00:32:06,730 好 这里的小抄呢 只是让大家先了解一下 671 00:32:06,730 --> 00:32:08,600 printf不只会把%s当然占位符 672 00:32:08,600 --> 00:32:13,490 它还会把%d作为十进制整数常数 或者%i也行 673 00:32:13,490 --> 00:32:19,450 %f是浮点数 %c是字元 如果你只是想 674 00:32:19,450 --> 00:32:23,510 把一个字符加入一个格式已定的字串里 就像我们一直在做的那样 你可以用%c 675 00:32:23,510 --> 00:32:28,490 然后 这个有点烦人 %lld是一个很长 很长的十进制整数常数 676 00:32:28,490 --> 00:32:31,050 这只表示 如果你需要一个很大的数 677 00:32:31,050 --> 00:32:35,450 然后你在用一个叫 long long的东西 我们在练习中还会看到 678 00:32:35,450 --> 00:32:40,700 你可以用%lld告诉printf在这里插入一个很大的整数 679 00:32:40,700 --> 00:32:41,830 经由它的第二或者其他的参数 680 00:32:41,830 --> 00:32:45,700 最后 我保证printf还支持 681 00:32:45,700 --> 00:32:47,670 一些其他的转义序列 682 00:32:47,670 --> 00:32:49,160 我们见过是\n 683 00:32:49,160 --> 00:32:50,510 \r 你们可以看到 684 00:32:50,510 --> 00:32:51,780 是个老派的东西 685 00:32:51,780 --> 00:32:55,920 如果你用过真的打字机 拉过曲柄的话 686 00:32:55,920 --> 00:32:59,810 它不只会跳到下一行 还会 687 00:32:59,810 --> 00:33:03,730 从右移至最左边 那 \r 688 00:33:03,730 --> 00:33:06,480 也会把你的光标移回行首 689 00:33:06,480 --> 00:33:07,830 而不会把它向下移 690 00:33:07,830 --> 00:33:09,780 但 以后 我们还会再讲 691 00:33:09,780 --> 00:33:13,670 \",\"",\\ 692 00:33:13,670 --> 00:33:16,600 是解决我先前提到的谜语的答案 693 00:33:16,600 --> 00:33:18,920 \0实际上挺有意思 694 00:33:18,920 --> 00:33:21,470 不过我们过一会会再来看 695 00:33:21,470 --> 00:33:25,640 所以现在 让我们来看CS50工具 696 00:33:25,640 --> 00:33:28,930 让我们用先前的例子来做个暖身 697 00:33:28,930 --> 00:33:31,160 然后来来看更复杂的事情 698 00:33:31,160 --> 00:33:34,980 所以 我打开了gedit这个程序 699 00:33:34,980 --> 00:33:36,840 这是我的图片编辑器 700 00:33:36,840 --> 00:33:38,460 我可以做这个 701 00:33:38,460 --> 00:33:39,820 让我来关掉这个窗口 702 00:33:39,820 --> 00:33:42,720 按这个图标 在gedit左下角的这个 703 00:33:42,720 --> 00:33:44,170 靠近菜单的 就可以打开gedit 704 00:33:44,170 --> 00:33:48,670 让我来把这个例子保存到 705 00:33:48,670 --> 00:33:49,660 John Harvard的文件夹 706 00:33:49,660 --> 00:33:52,690 John Harvard的文件夹只是它的目录 707 00:33:52,690 --> 00:33:53,340 他的文件默认保存到这里 708 00:33:53,340 --> 00:33:58,410 我会把这个命名为hello-0.c 709 00:33:58,410 --> 00:34:01,260 我选这个名字 是想要它和课程网站上的代码 710 00:34:01,260 --> 00:34:04,210 和YouTube的视频相符合 711 00:34:04,210 --> 00:34:06,320 现在 我要开始写我的程序了 712 00:34:06,320 --> 00:34:08,469 让我放大下让大家看得清楚 713 00:34:08,469 --> 00:34:12,760 让我来写下 int main void 714 00:34:12,760 --> 00:34:14,900 就像黄色拼图块一样 会开始运行这个程序 715 00:34:14,900 --> 00:34:18,290 我多年养成的习惯 是打下{} 716 00:34:18,290 --> 00:34:22,000 然后回到我想写下代码的地方 717 00:34:22,000 --> 00:34:24,630 只因为这样会让一切平衡得好 718 00:34:24,630 --> 00:34:25,880 特别是当程序写很长的时候 719 00:34:25,880 --> 00:34:29,239 现在 在这里 我会写 printf 720 00:34:29,239 --> 00:34:34,330 "hello, world \n "); 721 00:34:34,330 --> 00:34:38,100 我只是在重复我们到现在为止当作理所当然的而已 722 00:34:38,100 --> 00:34:39,270 现在我把它缩小 723 00:34:39,270 --> 00:34:43,030 底下的这个终端窗口 我可以 724 00:34:43,030 --> 00:34:44,389 在这个黑白窗口里做什么呢? 725 00:34:44,389 --> 00:34:46,977 我可以用它做什么呢? 726 00:34:46,977 --> 00:34:49,770 在这里我可以运行命令 也可以编译东西 727 00:34:49,770 --> 00:34:50,620 我会让一切保持简洁 728 00:34:50,620 --> 00:34:52,780 我要用一个叫Make的程序 729 00:34:52,780 --> 00:34:54,020 它严格意义上来说并不是一个编译器 730 00:34:54,020 --> 00:34:56,360 编译器叫做Clang 不过我们后面会再来说 731 00:34:56,360 --> 00:34:57,190 大概在一两周之后 732 00:34:57,190 --> 00:35:03,410 同在 我会输入 make hello-0 但是你们当中 733 00:35:03,410 --> 00:35:07,050 那些自己在头脑里比对我输入的和我应该输入的人 734 00:35:07,050 --> 00:35:10,180 会知道 我做错了什么 735 00:35:10,180 --> 00:35:12,160 很明显 这里有些问题 736 00:35:12,160 --> 00:35:16,742 在我去看错误是什么之前 有人知道我做错了什么么? 737 00:35:16,742 --> 00:35:18,590 [学生回答] 738 00:35:18,590 --> 00:35:18,840 没错 739 00:35:18,840 --> 00:35:20,640 我没有输入库的头文件 740 00:35:20,640 --> 00:35:24,240 这些以.h结尾的文件都可以叫做头文件 741 00:35:24,240 --> 00:35:25,680 它们共同属于"库" 742 00:35:25,680 --> 00:35:28,030 库是别人写下的代码 743 00:35:28,030 --> 00:35:32,140 所以标准的库是 744 00:35:32,140 --> 00:35:33,330 一批里面包含了其他人写下的代码的文件 745 00:35:33,330 --> 00:35:34,820 所以这个我忘了加 746 00:35:34,820 --> 00:35:36,520 那为什么我这出了错呢? 747 00:35:36,520 --> 00:35:40,840 让我在终端窗口这里往上拉一点 748 00:35:40,840 --> 00:35:44,310 不幸的是 在C语言 和其他很多编程语言里 749 00:35:44,310 --> 00:35:47,830 特别是在对你们来说新遇到的语言里 错误信息很准确 750 00:35:47,830 --> 00:35:48,620 但也很难懂 751 00:35:48,620 --> 00:35:51,720 这里的错误 用红字标出的 是 752 00:35:51,720 --> 00:35:57,660 "库函数printf类型不明" 然后看下一行 "int const char... * 753 00:35:57,660 --> 00:36:00,220 这很快就让人糊涂了 754 00:36:00,220 --> 00:36:04,420 但是你们应该开始做的 特别是如果是新遇到这问题 755 00:36:04,420 --> 00:36:06,010 是去找关键词 756 00:36:06,010 --> 00:36:08,770 显然 我所看到的一半文字 我都不懂 757 00:36:08,770 --> 00:36:10,140 不过你们 在一周内 就会懂 758 00:36:10,140 --> 00:36:11,230 但我看到了printf 759 00:36:11,230 --> 00:36:14,310 不久 这就应该能唤起你们的记忆了 好的 printf 760 00:36:14,310 --> 00:36:15,210 printf这有什么出问题了 761 00:36:15,210 --> 00:36:16,580 是我拼错了么? 762 00:36:16,580 --> 00:36:18,130 不是 看上去不是 哦 763 00:36:18,130 --> 00:36:21,350 在我教编译器说 它存在前 我不能使用它 764 00:36:21,350 --> 00:36:25,220 所以 再一次 相信你的直觉 即使你还 765 00:36:25,220 --> 00:36:26,510 不懂错误信息是什么 766 00:36:26,510 --> 00:36:30,240 而且这里 解决办法就是把它加到文件前头去 767 00:36:30,240 --> 00:36:34,340 像这样 用control-s 或者文件菜单重新保存 768 00:36:34,340 --> 00:36:36,730 现在如果我回到这里 我要清空这个 769 00:36:36,730 --> 00:36:39,150 control-L是一个简便的清空屏幕的方法 770 00:36:39,150 --> 00:36:44,870 然后我会输入 make hello-0 回车 771 00:36:44,870 --> 00:36:47,710 现在我看到了一堆难懂的符号 不过我们后面会再说 772 00:36:47,710 --> 00:36:49,230 这是Make帮你的结果 773 00:36:49,230 --> 00:36:52,590 它会自动 用Clang 也就是编译器 774 00:36:52,590 --> 00:36:54,050 接受一个很讨人厌的命令 775 00:36:54,050 --> 00:36:57,460 但事实上 我没有得到任何报错 就意味着这应该成功了 776 00:36:57,460 --> 00:37:00,630 那么现在我要 - 让我再放大一次 777 00:37:00,630 --> 00:37:07,070 ./hello-0 回车 的确 我看到了"hello, world." 778 00:37:07,070 --> 00:37:12,105 那让我们再稍微加强一下 再重复一次步骤 779 00:37:12,105 --> 00:37:15,370 我要用另存为将它重新命名为 hello1.c. 780 00:37:15,370 --> 00:37:20,300 现在我要声明一个变量叫name 所以是 string name 781 00:37:20,300 --> 00:37:23,420 然后我要赋它一个值 782 00:37:23,420 --> 00:37:27,030 也就是D-A-V-I-D "; 783 00:37:27,030 --> 00:37:31,150 然后我要把world替换成字串 要用什么占位筌符? 784 00:37:31,150 --> 00:37:32,200 %s 785 00:37:32,200 --> 00:37:35,040 现在 printf有多少个参数? 786 00:37:35,040 --> 00:37:35,700 两个 787 00:37:35,700 --> 00:37:37,090 那我就到引号外去 788 00:37:37,090 --> 00:37:39,330 我在逗号后输入 "name" 789 00:37:39,330 --> 00:37:41,550 但这次 我又犯了另外的错误 790 00:37:41,550 --> 00:37:43,940 但就假装我没注意到它 791 00:37:43,940 --> 00:37:44,910 让我们到这里 792 00:37:44,910 --> 00:37:48,850 注意 我输入"make hello 0" 这个输得有点烦了 793 00:37:48,850 --> 00:37:50,180 要打make还有那么一长串 794 00:37:50,180 --> 00:37:53,270 结果 在Linux里 你可以在键盘上敲击向上键 795 00:37:53,270 --> 00:37:56,850 然后就可以滚动浏览 796 00:37:56,850 --> 00:37:58,830 我前面到现在执行过的所有命令了 797 00:37:58,830 --> 00:38:02,040 所以如果我 向上 向上 make hello 0在这里 798 00:38:02,040 --> 00:38:02,610 我不想要它 799 00:38:02,610 --> 00:38:05,450 我想要把它改成hello 1 回车 800 00:38:05,450 --> 00:38:07,620 最终 它会帮你省些时间 801 00:38:07,620 --> 00:38:08,150 好的 802 00:38:08,150 --> 00:38:09,520 不幸的是 这里有错误 803 00:38:09,520 --> 00:38:10,980 所以让我往上滚动一点 804 00:38:10,980 --> 00:38:13,120 看上去我真的把程序弄坏了 805 00:38:13,120 --> 00:38:16,530 我的意思是 天 只有两行代码 但有10行错误 806 00:38:16,530 --> 00:38:18,800 不过我们先来看最上面 807 00:38:18,800 --> 00:38:21,640 使用未声明标识符 808 00:38:21,640 --> 00:38:23,270 我是想说 标准 i n吗? 809 00:38:23,270 --> 00:38:23,930 不是 810 00:38:23,930 --> 00:38:24,940 我是想说字串 811 00:38:24,940 --> 00:38:29,390 但是声明的字串变量类型在哪里呢 812 00:38:29,390 --> 00:38:30,730 在CS50库里 813 00:38:30,730 --> 00:38:33,740 所以在前几周里 814 00:38:33,740 --> 00:38:34,860 用C语言里有的还不够 815 00:38:34,860 --> 00:38:38,120 我还要再到上面这来 我可以把它放在上面或下面 816 00:38:38,120 --> 00:38:40,340 但我就按字母顺序排好了 817 00:38:40,340 --> 00:38:43,470 我要把cs50.h包括进来 818 00:38:43,470 --> 00:38:44,900 它已经在你们的CS50工具里了 819 00:38:44,900 --> 00:38:47,640 它是开源的 所以在网络上的人 820 00:38:47,640 --> 00:38:48,450 也可以在自己的电脑上使用 821 00:38:48,450 --> 00:38:50,700 但它是包括在CS50工具里的 822 00:38:50,700 --> 00:38:55,320 现在 让我回去重新用make hello 1编译 823 00:38:55,320 --> 00:38:55,710 糟糕 824 00:38:55,710 --> 00:38:57,240 还是有个错误 825 00:38:57,240 --> 00:38:59,370 让我滚动到第一个来 826 00:38:59,370 --> 00:39:00,630 不过这个有点复杂 827 00:39:00,630 --> 00:39:03,830 多字符常数 828 00:39:03,830 --> 00:39:04,890 这个对我没有帮助 829 00:39:04,890 --> 00:39:08,220 但是注意 Clang至少要好点 830 00:39:08,220 --> 00:39:11,890 它用一个绿色脱字号表示 这里我做错了 831 00:39:11,890 --> 00:39:16,160 为什么绿色小箭头指向的 832 00:39:16,160 --> 00:39:18,290 是我名字旁边的单引号? 833 00:39:18,290 --> 00:39:20,880 这是个你们得学会习惯的事 834 00:39:20,880 --> 00:39:23,980 特别是如果你用Python 或 Java Script或其他语言编过程 835 00:39:23,980 --> 00:39:24,560 在那里 这样的细节不重要 836 00:39:24,560 --> 00:39:25,740 在但C语言里 它很重要 837 00:39:25,740 --> 00:39:29,520 如果你在声明一个字串 这个字串是0或更多字符组成的序列 838 00:39:29,520 --> 00:39:32,280 你必须用双引号 839 00:39:32,280 --> 00:39:36,670 所以我要把这里改成一对 840 00:39:36,670 --> 00:39:37,800 双引号 841 00:39:37,800 --> 00:39:41,610 单引号也是有用的 但只有在你使用 842 00:39:41,610 --> 00:39:44,100 单个字元时 这个我们会另找时间来讲 843 00:39:44,100 --> 00:39:46,550 现在 双引号是必须的 844 00:39:46,550 --> 00:39:50,460 所以现在 让我回到我的终端窗口 make hello 1 845 00:39:50,460 --> 00:39:51,450 有谁有信心 846 00:39:51,450 --> 00:39:53,800 这次程序会正确编译? 847 00:39:58,250 --> 00:39:58,540 好的 848 00:39:58,540 --> 00:39:59,780 有三个人 849 00:39:59,780 --> 00:40:00,280 好的 850 00:40:00,280 --> 00:40:01,190 回车 851 00:40:01,190 --> 00:40:02,440 它真的成功了 852 00:40:02,440 --> 00:40:05,130 这次没有错误出现 853 00:40:05,130 --> 00:40:05,840 虽然程序变得更复杂了些 854 00:40:05,840 --> 00:40:10,110 如果我现在输入 ./hello 1回车 它就会说"hello, David." 855 00:40:10,110 --> 00:40:11,750 但让我们来做第三个版本 856 00:40:11,750 --> 00:40:13,380 这里程序会真正多元化 857 00:40:13,380 --> 00:40:16,770 让我把文件名换掉 858 00:40:16,770 --> 00:40:20,410 这只是为了让文件名和你们后面在网上看到的 859 00:40:20,410 --> 00:40:21,620 回车 860 00:40:21,620 --> 00:40:25,510 现在我要在里面 不想存储硬编码"David" 861 00:40:25,510 --> 00:40:28,826 我要怎样才能很方便地改进这个程序? 862 00:40:28,826 --> 00:40:30,520 我可以用getstring 863 00:40:30,520 --> 00:40:33,240 下面要发生什么可能不太明显 864 00:40:33,240 --> 00:40:38,470 所以我实际上要加一行 printf 然后 name:" 865 00:40:38,470 --> 00:40:40,790 只是要在屏幕上给用户一个提示 866 00:40:40,790 --> 00:40:42,980 现在我要到这里 我要用键盘上的快捷键 867 00:40:42,980 --> 00:40:47,680 我要向上 向上 然后把hello1 改成 hello2 回车 868 00:40:47,680 --> 00:40:49,260 谢天谢地 我有取得进展 869 00:40:49,260 --> 00:40:52,720 然后我会到./hello这里 870 00:40:52,720 --> 00:40:54,690 把它变成2 回车 871 00:40:54,690 --> 00:40:55,650 现在我的程序 872 00:40:55,650 --> 00:40:57,700 我会放大它 -- 变得漂亮些了 873 00:40:57,700 --> 00:41:02,190 名字这次就 说是Rob好了 回车 hello Rob 874 00:41:02,190 --> 00:41:03,260 我们可以再做一次 875 00:41:03,260 --> 00:41:05,360 名字 Lauren 回车 876 00:41:05,360 --> 00:41:07,820 名字 Joseph 回车 877 00:41:07,820 --> 00:41:11,596 名字 让我们故意弄难搞一些 回车 878 00:41:11,596 --> 00:41:12,410 呃 879 00:41:12,410 --> 00:41:14,680 这倒不是一个bug 880 00:41:14,680 --> 00:41:16,090 只是长得不太好看 881 00:41:16,090 --> 00:41:18,640 我们在未来可能可以解决这个问题 不过不是现在 882 00:41:18,640 --> 00:41:20,840 不过凭直觉 你们会怎样 883 00:41:20,840 --> 00:41:21,990 处理这个挑战呢? 884 00:41:21,990 --> 00:41:23,710 它只是看上去有点蠢 885 00:41:23,710 --> 00:41:27,320 那怎样不要它看上去有点蠢呢? 886 00:41:27,320 --> 00:41:29,890 我们可以 好的 我听到了几个条件 条件 还有循环 887 00:41:29,890 --> 00:41:33,340 我们可以用 一、一个条件 去检查 888 00:41:33,340 --> 00:41:34,190 用户给我们的字串的长度 889 00:41:34,190 --> 00:41:37,100 如果是0 那就只是说 他们只是敲了回车 890 00:41:37,100 --> 00:41:38,930 那也许我应该冲他们喊然后再提示他们做一次 891 00:41:38,930 --> 00:41:39,970 但我要怎样再提示他们一次? 892 00:41:39,970 --> 00:41:44,230 我还听到有人说循环 我可以一遍又一遍 893 00:41:44,230 --> 00:41:47,010 去提示用户做相同的事 894 00:41:47,010 --> 00:41:48,880 让我们用CS50库里的另一个函数 895 00:41:48,880 --> 00:41:50,620 来做例子 896 00:41:50,620 --> 00:41:52,330 让我把这个文件关了 897 00:41:52,330 --> 00:41:53,510 来创建一个新的 898 00:41:53,510 --> 00:41:58,510 我要叫它 adder.c 因为这样做简单算法更方便 899 00:41:58,510 --> 00:42:00,890 虽然这对于任何一台现代电脑来说 900 00:42:00,890 --> 00:42:02,250 根本不算什么 901 00:42:02,250 --> 00:42:03,750 不过让我 -- 902 00:42:03,750 --> 00:42:10,680 从上次我吸取了教训 -- 加上cs50.h, stdio.h, 903 00:42:10,680 --> 00:42:14,170 int main void 现在 我只是盲目相信 904 00:42:14,170 --> 00:42:16,380 但就让我们相信 不久我们会理解这些是什么意思 905 00:42:16,380 --> 00:42:21,030 然后我会说 "给我一个整数" 906 00:42:21,030 --> 00:42:22,140 那现在 我要怎样得到一个整数? 907 00:42:22,140 --> 00:42:26,820 最终 我想声明一个叫x的变量 它的类型是整数 908 00:42:26,820 --> 00:42:29,240 然后把它储存在用户提供的整数里 909 00:42:29,240 --> 00:42:29,970 这个真是有点复杂 910 00:42:29,970 --> 00:42:35,680 不过如果有人想提议 我该怎样声明一个叫x的整数呢? 911 00:42:35,680 --> 00:42:36,310 int x. 912 00:42:36,310 --> 00:42:37,430 就是这么简单 913 00:42:37,430 --> 00:42:38,240 给我一个整数 914 00:42:38,240 --> 00:42:39,070 叫它x 915 00:42:39,070 --> 00:42:40,580 现在我要用赋值运算符 916 00:42:40,580 --> 00:42:43,400 我要怎样把左边用户给的值储存起来? 917 00:42:43,400 --> 00:42:47,310 显然 我不想用getstring 我要用getint 918 00:42:47,310 --> 00:42:49,060 有参数么? 919 00:42:49,060 --> 00:42:49,350 没有 920 00:42:49,350 --> 00:42:51,870 所以是() 921 00:42:51,870 --> 00:42:53,440 然后用;结束这一行 922 00:42:53,440 --> 00:42:54,930 让我再做一次 923 00:42:54,930 --> 00:42:57,070 给我另一个整数 924 00:42:57,070 --> 00:43:01,850 这一次 我把这个整数叫y = getint 925 00:43:01,850 --> 00:43:05,060 下面让我做一件特别简单的事 像数学里一样 926 00:43:05,060 --> 00:43:14,340 printf %d的总和 %d是一个整数的占位符 927 00:43:14,340 --> 00:43:20,030 %d is %d.\n 928 00:43:20,030 --> 00:43:20,360 好的 929 00:43:20,360 --> 00:43:21,670 事实上这不是数学 930 00:43:21,670 --> 00:43:25,500 但是如果我想要说 这个值的总和加上这个值等于另外这个值 931 00:43:25,500 --> 00:43:29,320 我一共要给printf多少个参数? 932 00:43:29,320 --> 00:43:30,520 一共要多少个? 933 00:43:30,520 --> 00:43:31,420 4个 对不对? 934 00:43:31,420 --> 00:43:33,790 这个字串 然后3个值 935 00:43:33,790 --> 00:43:37,560 x是我第一个想要放到%d里的 936 00:43:37,560 --> 00:43:39,270 y是第二个 937 00:43:39,270 --> 00:43:42,030 然后我现在想说z 但是z并不存在 938 00:43:42,030 --> 00:43:43,190 但这不重要 939 00:43:43,190 --> 00:43:45,440 因为你本能就会 940 00:43:45,440 --> 00:43:47,820 特别是如果你有一个绘图计算器的话 会在里面输入什么? 941 00:43:47,820 --> 00:43:48,760 x加y怎么样? 942 00:43:48,760 --> 00:43:50,200 所以这并不是一个真正的变量 943 00:43:50,200 --> 00:43:51,820 它是两个变量的和 944 00:43:51,820 --> 00:43:53,170 这完全合理 945 00:43:53,170 --> 00:43:56,630 C语言当然会理解这么简单的算法 946 00:43:56,630 --> 00:43:58,450 ; 保存 947 00:43:58,450 --> 00:44:02,080 现在让我到下面 输入make adder 回车 948 00:44:02,080 --> 00:44:04,100 没有错误 这也是一项进展 949 00:44:04,100 --> 00:44:04,890 输入adder 950 00:44:04,890 --> 00:44:07,100 另一个键盘快捷键 951 00:44:07,100 --> 00:44:08,760 如果你对这么多命令感到厌烦的话 952 00:44:08,760 --> 00:44:12,650 如果你开始输入一个命令 像是./ad 953 00:44:12,650 --> 00:44:16,020 在这你觉得有烦 你可以敲tab键 954 00:44:16,020 --> 00:44:20,510 然后让电脑帮你输入完 如果你知道ad之后的东西是什么的话 955 00:44:20,510 --> 00:44:23,950 所以让我按回车键 956 00:44:23,950 --> 00:44:27,490 给我一个整数 1 2 谢天谢地 是3 957 00:44:27,490 --> 00:44:29,900 但同样 测试程序 958 00:44:29,900 --> 00:44:30,820 不能只试一次 959 00:44:30,820 --> 00:44:35,560 让我来试一个特例 像是-1 给我1 960 00:44:35,560 --> 00:44:36,210 这一个也成功了 961 00:44:36,210 --> 00:44:38,870 我应该再做些测试 962 00:44:38,870 --> 00:44:40,630 但我对它现在的状况有信心 963 00:44:40,630 --> 00:44:43,110 现在 让我们再来试另一个程序 964 00:44:43,110 --> 00:44:44,620 里面用了不同的句法 965 00:44:44,620 --> 00:44:46,100 让我来建一个新文件 966 00:44:46,100 --> 00:44:51,050 我叫它 conditions0.c 和网上的代码范例相统一 967 00:44:51,050 --> 00:44:55,550 让我加上cs50.h 968 00:44:55,550 --> 00:45:00,320 stdio.h, int main void 969 00:45:00,320 --> 00:45:01,030 好的 970 00:45:01,030 --> 00:45:01,850 这下行了 971 00:45:01,850 --> 00:45:03,010 我们有了自己的标准样版 972 00:45:03,010 --> 00:45:08,170 这一次 我要对printf说 我想要一个整数 973 00:45:08,170 --> 00:45:10,030 只是为了让这个提示看上去更亲切些 974 00:45:10,030 --> 00:45:11,620 现在我想要从用户手上得到一个整数 975 00:45:11,620 --> 00:45:15,010 这一次我要叫它n 因为n听上去像数字(number) 976 00:45:15,010 --> 00:45:18,140 getint 现在 我想要做什么呢? 977 00:45:18,140 --> 00:45:21,640 如果n是 -- 我要放大一下 -- 978 00:45:21,640 --> 00:45:25,930 如果n比0大 我想要做以下事 979 00:45:25,930 --> 00:45:36,060 printf “你选择了一个正数” 否则 我会在printf里键入 980 00:45:36,060 --> 00:45:37,870 "你选择了一个负数" 981 00:45:37,870 --> 00:45:39,650 好的 982 00:45:39,650 --> 00:45:44,410 这个程序 虽然我很快写出了它 从句法上讲 看上去是对的 983 00:45:44,410 --> 00:45:45,010 让我们试一下 984 00:45:45,010 --> 00:45:46,890 Make condition 0 985 00:45:46,890 --> 00:45:47,710 看上去运行成功了 986 00:45:47,710 --> 00:45:49,230 Condition 0 回车 987 00:45:49,230 --> 00:45:51,910 让我们给它50这个整数 988 00:45:51,910 --> 00:45:53,160 我选了一个正数 989 00:45:53,160 --> 00:45:54,230 再试一次 990 00:45:54,230 --> 00:45:54,930 Condition 0 991 00:45:54,930 --> 00:45:56,260 -50 992 00:45:56,260 --> 00:45:57,290 我选了一个负数 993 00:45:57,290 --> 00:46:00,350 但现在 让我们选一个特别的 994 00:46:00,350 --> 00:46:04,702 这个例子可能会给你们带来想像不到的麻烦 0 995 00:46:04,702 --> 00:46:07,940 我很确定 这是个特例 996 00:46:07,940 --> 00:46:12,330 因为0既不是正数也不是负数 我的程序虽然句法上是正确的 997 00:46:12,330 --> 00:46:15,440 编译了 也运行了 逻辑上来说 是不对的 998 00:46:15,440 --> 00:46:21,050 这里简单的解决办法 让我能发现问题 999 00:46:21,050 --> 00:46:23,840 让我能解决 n=0 的问题的是什么呢? 1000 00:46:23,840 --> 00:46:32,980 如果n=- 那么我想对printf说 "你选择了0" 1001 00:46:32,980 --> 00:46:33,990 让我来试一下 1002 00:46:33,990 --> 00:46:38,320 让我回到这里 清除窗口 然后重新编译 1003 00:46:38,320 --> 00:46:38,690 嗯 1004 00:46:38,690 --> 00:46:39,770 出现了一个错误 1005 00:46:39,770 --> 00:46:41,630 但我是想要检查n是不是等于0 1006 00:46:44,230 --> 00:46:47,720 所以又一次 我们要适应这个 1007 00:46:47,720 --> 00:46:48,910 等于号是赋值运算符 1008 00:46:48,910 --> 00:46:52,240 所以这里有个错误 虽然编译器不让我犯错 1009 00:46:52,240 --> 00:46:56,450 但是我们实际上是把0复制到了 n里 1010 00:46:56,450 --> 00:46:57,590 而这不是我想要的 1011 00:46:57,590 --> 00:47:00,950 看是不是等于的话 要用== 1012 00:47:00,950 --> 00:47:03,390 所以这大概是更好的解决方法 1013 00:47:03,390 --> 00:47:07,810 让我重新把它保存成 condition 1 它是一个新的 改进后的版本了 1014 00:47:07,810 --> 00:47:10,480 如果我现在重新编译它 make conditions 1015 00:47:10,480 --> 00:47:16,960 -- 哎哟 -- make conditions1./conditions1 回车 1016 00:47:16,960 --> 00:47:18,760 "请给我一个整数" 我来输入50 1017 00:47:18,760 --> 00:47:19,660 它还是好的 1018 00:47:19,660 --> 00:47:21,200 -50 好的 1019 00:47:21,200 --> 00:47:24,920 0 它确实探到我选择了0 1020 00:47:24,920 --> 00:47:28,200 那现在我还可以做什么呢? 1021 00:47:28,200 --> 00:47:32,280 当然 我们可以做更复杂的事 1022 00:47:32,280 --> 00:47:37,240 但我提议 就到此为止 1023 00:47:37,240 --> 00:47:41,680 让我们看这里 这可能是部你们最喜欢的卡通 1024 00:47:41,680 --> 00:47:43,326 但今天以前你们可能都看不懂 1025 00:47:46,550 --> 00:47:49,520 CS50课上的笑话差不多就是这个水准了 1026 00:47:49,520 --> 00:47:51,060 好的 1027 00:47:51,060 --> 00:47:54,900 但是更重要的 是我想我应该给周一留个悬念 1028 00:47:54,900 --> 00:47:56,430 所以我们开始用整数了 1029 00:47:56,430 --> 00:47:57,510 我们提到了浮点数 1030 00:47:57,510 --> 00:48:00,120 我们还提了双精度浮点数 它 也一样 1031 00:48:00,120 --> 00:48:01,390 在小数点后有数字 1032 00:48:01,390 --> 00:48:04,230 但是 浮点数和双精度浮点数 以及电脑 1033 00:48:04,230 --> 00:48:08,470 从本质上来说 都不能准确地表达某些数值 1034 00:48:08,470 --> 00:48:12,840 从数学课上 我们知道 在小数点后 1035 00:48:12,840 --> 00:48:13,510 你可以放任意长的数字 1036 00:48:13,510 --> 00:48:16,460 你还可以用一个|表示 小数点后有无限个数 1037 00:48:16,460 --> 00:48:18,810 不幸的事 在电脑里 你不能这么做 1038 00:48:18,810 --> 00:48:20,400 那么 特例就来了 1039 00:48:20,400 --> 00:48:23,120 比如说 假设你在做有关金融的事 1040 00:48:23,120 --> 00:48:25,700 你在计算百分比 1041 00:48:25,700 --> 00:48:27,030 涉及到元 与分 1042 00:48:27,030 --> 00:48:29,230 而这些几分钱并不是整的 1043 00:48:29,230 --> 00:48:34,120 我们发现 不是整的几分钱 1044 00:48:34,120 --> 00:48:37,250 放在电脑系统里的话 会让聪明的人 1045 00:48:37,250 --> 00:48:39,100 有机可乘 1046 00:48:39,100 --> 00:48:42,650 对于你们中还没有看过的人 我想花30秒提下 1047 00:48:42,650 --> 00:48:48,290 一个很好的电影 叫Office Space 它就讲了一个 1048 00:48:48,290 --> 00:48:51,205 我们在周一一开始就要解决的问题 1049 00:48:51,205 --> 00:48:53,960 如果我们能把音量调大些 1050 00:48:53,960 --> 00:48:55,495 我给你们看30秒的Office Space 1051 00:48:55,495 --> 00:48:55,770 [视频播放] 1052 00:48:55,770 --> 00:48:59,000 我说 你都一直没上班 你要保住你的工作 1053 00:48:59,000 --> 00:49:00,620 事实上 我被升职了 1054 00:49:00,620 --> 00:49:03,240 我可以写出一个病毒 让这里遭受巨大损失 1055 00:49:03,240 --> 00:49:04,430 那 是怎样做到的? 1056 00:49:04,430 --> 00:49:06,782 每一次银行交易 计算利息时 1057 00:49:06,782 --> 00:49:10,160 这样的交易每天有几千笔 电脑会算出不是整的几分钱 1058 00:49:10,160 --> 00:49:12,030 我才不要做违法的事 1059 00:49:12,030 --> 00:49:12,660 违法? 1060 00:49:12,660 --> 00:49:15,075 Samir 这是美国 1061 00:49:15,075 --> 00:49:16,570 我们得对上帝发誓的 1062 00:49:16,570 --> 00:49:19,070 如果除了我们没人知道这事 1063 00:49:19,070 --> 00:49:21,920 家庭成员 女朋友 都不行 1064 00:49:21,920 --> 00:49:22,700 当然 1065 00:49:22,700 --> 00:49:23,320 同意 1066 00:49:23,320 --> 00:49:24,280 别担心伙计 1067 00:49:24,280 --> 00:49:25,640 我也不会告诉任何人的 1068 00:49:25,640 --> 00:49:26,504 [视频播放结束] 1069 00:49:26,504 --> 00:49:30,030 好 这是CS50 刚才的是Office Space 1070 00:49:30,030 --> 00:49:31,465 在周一 你们就会理解这一切了 1071 00:49:31,465 --> 00:49:33,940 下周见 1072 00:49:33,940 --> 00:49:37,880 在下一周的CS50里 1073 00:49:37,880 --> 00:49:39,130 Rob要学会适应不再在食堂吃东西的生活