[音乐]
好的
欢迎回来
这里是CS50 我们继续第一周的课
我们向大家介绍很多科技产品 例如Google眼镜的原因之一
就是它们要用到API
也就是应用程序接口
这就是说 有了正确的文件与记录
你也可以写出给这些产品的软件
所以我们在学期末想要达到的目标就是
如果一个产品正在不断发展 例如 Google眼镜可能会有兴趣
我们会看看能不能借到一些硬件 以及
对公众开放的API 这样你们就可以开始自己做出
可以在Google眼镜中运行的软件了
我们一直玩的另外一个小装置 我们想
在学期末大概会很有意思 就是这个小玩意
它叫做Leap Motion 你们马上将要看到的
是这个产品的广告 但是它能很好地展示
这个产品的功能
它 也一样 有API 而你可以通过API自己写出软件
来控制你的电脑 就像这个一分钟的视频里展示得那样
[播放视频]
[视频结束]
现在 这个装置被放在USB上
你可以将它连到自己的电脑上 不过我想
不久之后 这种技术就应该存在于新一批PC和MAC上了
这样我们就可以 真真正正地
与它进行交流互动
事实上 我下面要做的 是展示一个
能在这个软件上运行的程序
我把这个传感器放在我的笔记本前面
正如你们所看到的 它意识到了我的手在这里
控制着它
而这个装置可以流畅地进行这样的识别
不过你们可以看到 现在我5根手指在这儿
如果我们放10根 你可以想像有一个钢琴
或者其他类似的app
所以如果你今天课后想来试用一下
我们十分欢迎
在今后的课上我们还会持续关注它
一些课程通知
一 请在周五中午前完成你们的小班课 请点击 cs50.net/section
这个链接你们也可以在课程网站主页上找到
同时 这周日 超级小班课会开始
超级小班课是一个只会发生一次的课 因为我们安排
谁在什么时间在哪上课 很费工夫
所以这周日 会有一场课给那些不太适应这门课的人
另一场给那些比较适应这门课的人
而觉得自己居于两者中间的 可以随便去听任意一场
都去或者都不去
我们会录下两场课程
课会在一个较大的礼堂里上 比平时的小班课教室要大些
不过这课目的是让你们在这周末
在你们知道自己正式的小班课分组之前
能自如地面对练习一 C语言 及CS50工具
所以在差不多一周内 你们就能见你自己的助教
及小班课上的同学了
办公室时间会正常开放
请好好利用今晚及明晚的办公室时间
练习0周五要交
这比一般定的周四要晚一天
不过对于练习1 你们会发现其中包括了一些暖身题
所以你们可以把周四的截止日期
延长到周五
练习1 会在周五于课程网站上放出
这样你们就可以像我以前一样
在周五晚上就开始解决它了
在cs50.net/appliance上你可以找到我们在周一就开始用的
CS50的相关工具
我们今天会用得更多
不过我们保证 在练习1的说明里
你可以找到详细的指示 教你怎样安装它
所以如果你在练习1放出前还没有装的话 也不用担心
好的
我们在周一看了源代码 不过是在
C语言的环境下看的 C语言和Scratch不同 不是图象化的
它没有那些拼图块
它更像是英语的句法
在C语言中真正写出并运行一个程序之前 还有许多步要做
因为除了这样的源代码之外 你还需要
一个叫做编译器的东西
用普通人的话说 编译器是干什么用的呢?
你
它会把你写的代码转成一堆0与1
很好
所以它能把我们写的代码转换为0与1
它会把所谓的源代码转换为目标代码 而后者
长得像这样
你们的CPU 也就是电脑的大脑 因为造成这些电脑的人
才懂得这些0与1
的排列代表着什么
也许它代表 输出
也许它代表 添加
也许它代表 减去
也许它代表 展示一张图
这些都是预先设定好的排列 我们规定它们
代表一些特定的东西
但是在这门课里 大多数情况下 我们都在更高层面上看问题
所以我们就默认 这世上存在像编译器这样的东西
它们都让我们的源代码按我们想要的方式运作
所以我们在上周先写后运行的程序
也就是这周一在C语言里的那个程序 是这个
当点击绿色旗帜时 say hellp
这个 当然 是在Scratch里写的
而我说 与之相对应的程序在C语言里
会长得像这样
所以我想 我们先来分解一下
第一眼看时 确实 它是很难懂的句法 但是你很快
会开始发现一些规律
同样 我们在今天要做的 是让你们对一些
标准的编程构造有思想准备
然后 我们就会就一些例子来进行实践
所以对于那些对课程更得心应手的同学来说 你们会发现
这周与下一周的内容可能对你们来说是一个复习
但是当练习1的黑客版本于周五上线时
我相信你们会觉得
虽然这两周对于你们是查漏补缺 但是你们也会
渐渐发现课程越来越有挑战性 学到的新东西越来越多
所以 让我们把这个程序分成几行一段来看
在最上面 我们可以看见
一个叫做 预处理器的东西
这个是一行代码 说要包括文件内容
也就是标准的I/N 标准输入-- 不好意思 应该是标准的I/O.h
也就是我程序中 标准的输入/输出
换句话来说 如果我在一个更简单的文件处理器里写这个程序
比如说 gedit 一个Microsoft Word的简单版
这个指令 #include stdio.h 就会告诉编译器
把另一个文件 stdio.h中的内容抓取出来 然后贴在这里
那么现在 我为什么要在乎这个?
我们说 这个储存在我硬盘某处 或者在这里
储存在CS50工具某处的 叫stdio.h的文件里面到底有什么呢?
有别人帮我把它放在那儿了
但是 它里面有什么呢?
没错 它是这个函数 printf 的声明
printf 可以召回这个函数
在屏幕上按我的要求显示文字和数字
但是这不是我写的
也不是CS50写的
某个人很多年以前就写好它了 然后他把独家秘方保存在了
stdio.h这个文件里
所以这时的第一行 让我可以使用这个有人很多年前
就先写好的东西 里面包括printf
现在 下面这一行 我们在下周之前都不会碰
现在 只要知道 int main (void) 就等于
当点击绿色旗子时 这一块黄色的拼图块就行了
许多年前 人们就决定 如果你在用C语言写程序的话
你最一开始要写一行
这样子的东西
在差不多一周后 你们就会明白 int 是什么 void 是什么了
但是现在 只要把它们想成这块黄色的拼图就行了
下面 我们看到一对大括号
大致是这样
我们在C语言中会一直看见它们
在JavaScript和PHP中也一样
它们包覆住了相关的代码们
左大括号表示 下面会有一些代码
而右大括号 也就是与前者相反方向的
表示 相关联的代码就到此为止了
所以在第一个程序里的最有意思的是剩下的这一行代码
printf "hello world"
我上一次把引号中间的东西叫什么?
是字串
字串是个术语 表示
一个字符 词 或词组的序列
就算是一个字母 也可以放在引号中
但它是由零个或多个字符组成的序列
不过 \n 看着有点怪
不过它对电脑来说 是个很简单的东西
\n 代表什么呢?
新一行
所以 它是一种特殊排列的字符
我们决定让它代表 换行 因为如果你
很理所当然 但错误地 想按下回车键换行的话
就会把编译器弄糊涂
你得说得更明白些 所以要用\n
我们还会发现 其他一些特殊字符的规律
比如 如果你想让自己做点特别的
比如说我想要在屏幕上输出双引号
就会有点麻烦 是不是
因为我把一个引号放在了 "hello, world" 里
不管是出于什么原因 为什么这可能导致问题呢?
它打断了字串
编译器只是一个程序
像编译器这样的程序只会把你写的代码从上往下
从左往右读
如果它发现有3个双引号 而不是2个的话 它并不会知道
左边的这东西是一个字串 还是右边的
还是整个东西都是一个字串
太模棱两可了 所以一般的编译器就会发疯
然后向你报错 逼你来解决这个问题
所以 加上\n 表示开始新一行
现在 凭直觉 你想要在这个已经包含在引号里的字串里加一个特别的东西
比如一个双引号 你应该怎么做?
\"
我们还会看到这种规律
如果你想做点奇怪的事 你会发现
解决的办法往往都遵从某种规律 而\
表示一个转义序列
这表示某种特殊的东西 我们需要
用一个不同的方法来表示它
至少于要怎么样表示\
我们以后会说到
不过这个问题的答案 其实 也很明显
现在 我们来介绍另一些你们在Scratch中
看到的编程结构
如果你们已经开始做练习0的话
你也许已经习惯它们了
但是现在 我们要来介绍这些本来很易懂的想法
在C语言里是怎样的 大部分我们都会讲到
所以这里的条件 或者分支
我们先前是用左边这样的代码在Scratch里表示的
如果x小于y 那么就显示这个
不过现在在C语言中 让我举更简单些的例子
比如说 如果 左括号 某件事是真的--我在提出条件时
就要在这放一个布尔表达式--右括号
那么就做大括号内的事
所以这里 同样 大括号就有点像Scratch里的
U字型拼图块
做这些大括号内的事
在这里 也就是//
请注意 在这里 是正斜线 不是反斜线
// 对于那些编过程的人来说
意味着
这只是一个评论
评论不是一行代码
它是一行英文 是你这个人给自己写的
以提醒自己去做某事 或向自己或他人解释
你的代码在做什么
它只是一个描述性的评论
现在 当然 我们这样做会有一个分叉路口
我们也可以创造一个三岔路口 如果你继续下去
你也可以有一个四岔 五岔 六岔路口
是的 如果你想要设这么多的条件的话
请注意这里的这种并行结构
if (条件) else if (条件) 唯一有点奇怪的是剩下的一个
也就是else
不过 这从概念上来说 和我们先前所做的
是一样的 虽然句法需要我们花点时间来适应
现在 在这个例子里 另有些的句法
我们又看到了printf 括号
在里面是一个带引号的字串
在printf里 括号中的
是什么东西
我们把这个引号中的字串统称为什么?
是参数
任何时候 如果你有一个函数 比如说 printf
以及括号包住的某种东西 不论是字串
还是整数 或者是其他的什么 这个在括号中的东西
就叫做参数
而参数能影响函数行为特征
在这里 很明显
x<y 是这里的参数
而printf会把它输出
因为很显然 那个很多年前设计出printf的人
没有想到后人会用printf做什么
所以才有参数存在 这样它就能改变已经写好的函数
的行为特征了
好的 布尔表达式
我们在Scratch中看到过它
而在C语言中 我们发现 你也可以用"或者"把它们联在一起
两条竖线表示这一段代码 会运行
如果第一个条件为真 或第二个条件为真的话
虽然 你可以没在Scratch里做过这个
你是可以在Scratch里完成它的 而且 你还可以用不同方式表达自己 --
如果第一个和第二个条件为真
那么就执行在大括号中的事
补充一句 它之所以是有两个 &
和两个| 是因为 一个 | 和一个&
实际上在C语言中有不同含义 所以现在
符号出现两遍是我们有意为之的
那么 让我们简单介绍一下其余的句法
这和Scratch中好像没有对应 但是
我可以用一个 if, else if, else if, else if, else语句来完成这件事
而这个语言也就是交换语句
而它存在的意义就是给你 也就是编程者
提供一种不同的解决问题的方法 虽然逻辑上来说
它并没有给你任何新的能力
我说这个的意思
是当你说 switch (交换) 空格 然后一对括号
里面放着一个表达式
这个一开始可能看着不是很明显 但严格意义上来说
它不是一个参数 因为 switch 不是一个函数
所以现在 就认为我们是在不同情境下
不同理由下用括号好了
所以 根据表达式交换 表示 我可以在这些括号中
也就是表达式里 放一个变量
如果这个变量
假如说它叫x 然后它是一个整数
那我就可以在我的这页幻灯片上计算这样一个东西
如果x是我的变量 而我想说
如果x=1 那么我就可以这样做
而如果x=2 那么我就可以那样做
否则 如果我想做另外一件不同的事
那我就有一个默认的情况 让我去做那件事
所以 这就等同于一个 if x==1 , else if, else语句
我在这里提到它 是因为我们后面还会看到
不过现在 只用知道它存在就行了
好的
最后这一些一开始看上去会有点复杂
但是它们做的事很明了
C语言中的For循环 是一堆代码
一直重复做某件事
唯一让我们不高兴的 是想要在循环中表达自己
有点难 不过表达方法
是全球通用的
当你用一个for循环的时候 你又得用括号了
而且注意 这里有两个分号
这两个分号隔开了括号中
三个不同的表达式
一个是所谓的初始化
一个是所谓的条件
最后一个是所谓的更新
抽象来看 这个很难理解
所以让我们来看一个具体的例子
在Scratch中我们有一个重复的块
这个拼图块表示 重复10次 输出 "hello, world" 让我
现在先提它一下 我们以后还会再讲到它 而你们也会
越来越熟悉它 在C语言中与之等同的代码
可以是这个 -- 一个for语句 空格 一对括号 然后请注意
其中的分号分隔开了三样东西 初始化
条件 和更新
猜一猜第一个是做什么的
Int i = 0
用一般人的话来说 这个大概是干什么的?
没错
它是在声明 有个变量 i 而我们给了它什么值?
0
所以 它创造了一个变量 i 在其中存储了0这个值
这就叫做初始化
好的 在中间的
i < 10 就是条件
它是做什么的呢?
一个带for循环的程序运行时 for循环所做的
就是电脑每跑过这个循环一次 从上至下
就像你们站起来数数再坐下那样
一遍又一遍 电脑会检查
这个条件
如果i确实小于10 它就会再重复一次
如果i还小于10 它就会再重复一次
这好像表示 理想情况下 i在不断变化
否则 我们就会有一个无限循环了
确实 i是在变化 因为最后一个在分号后的东西
看上去很难懂 是i++
不过 对于你们中写过这个的人来说
这个代表着什么?
[学生回答]
给i加1
增加1
实际上 我们看到过这个的Scratch拼图块
它看上去不像i++ 不过它表示
每一次 给i增加1
所以 一开始 你给i的值是0
然后你就检查你的条件
0与10小么?
是的
我们再经过循环一次
而电脑要做的下一件事
是给i加1
所以现在i是1
它会检查条件
1比10小么?
当然
所以它就再重复一次
于是它把i++加到了2
2比10小么?
是的
然后重复再重复
直到加呀加 我们得到了i等于10
10比10小么?
当然不对
这一刻 这个循环停止了
然后你后面还有别的代码
那么电脑就会接着运行那些代码了
所以又一次 虽然一开始 对于没有编过程的人来说
这看上去有点奇怪
它其实就是把Scratch里简单明了的拼图块
抽象转换了而已
好的 我说了还会有另一个与Scratch中类似的东西
这里就是一个
我们上一次也短暂地看过
还记得无限循环块在Scratch里是什么?
是无限次地做某件事么?
我保证你这样也可以做到
实际上 你有很多种方法都可以做到
而while循环只是在C语言中 一个不同的 表达自己的方式
到头来 你用for循环能做到的
while循环也能做到
所以 它们在功能上实际上是一样的
不过它们可以让你用不同方式表达自己
像下面这样
while循环 是括号中的东西被
一次又一次检查
如果表达式为假 那么循环就停止了
电脑会运行文件里
你写在后面的代码
在这里有意思的是 我真的输入了 true
而true是布尔值 也就是 真 或 假 中的一个
所以 true有没有可能变假呢
如果我就在我们程序里硬编码的话?
不会
我这样做有点奇怪
但是true 就是真
没有什么++ 的改动
这里没有变量
因为我硬编码 while true 这个循环会一直评估
一遍又一遍
那么hello world会在屏幕上出现多少次?
无限次
直到电脑没电了
或者其他什么事发生
所以这不是最好的程序 因为如果用户无法
退出你的程序 这大概不会是你想要的
不过有时 程序应该有个无限循环
比如 你的电脑里有时钟
如果它能一直自己更新的话 当然比较好
虽然是每一秒 或者每分钟更新
所以无限循环也是有它存在的意义的
好的
最后 这后
这一个从功能上来说略有不同
在练习1中我们可能还会讲到它
但有另一种循环 叫 do while循环
这个循环与while循环的唯一区别
是 do while循环当中的条件 只有在你执行代码后
才会被检查
所以 对于while循环来说是从上开始的
而对于do while循环来说 是从底端开始的
这样的话do while循环一般会比while循环[英文漏字]
多运行还是少运行几次呢?
应该是多运行 因为一个do while循环中
是只有在你检查了括号内的条件是否为真后
再执行
我们会在练习1中再次看到这个
如果你想至少做某事一次 然后以后再做几次的话
用这个结构会比较好
但是一个while循环 相反 会先检查条件
那么C语言中的循环就讲到这里 我们讲了do while, while和for循环
另外 它们也有Scratch对应得很好 除了
这个以外 在Scratch中都有对应
那么 现在来看看变量吧?
我上一次是这样用句法来表示变量的
我用了 int 我说它表示一个整数
然后我放了一个变量 叫counter 然后是分号
所以这一行代码在做什么呢?
很简单 它在声明变量
也就是告诉电脑 给我一些空间 一些比特
来让我储存什么呢?
一个int
而这个分号只是表示 这一行代码结束了
现在 第二行就应该很容易猜了
counter = 0; 是在干什么?
是把0赋予counter
这个是 和 呃 代数相比
又一件有点烦人的事
在代数中 等于号表示等于 而在C语言里
等于号表示赋值
所以它表示 把等号右边的东西放到左边去
我们会看见另一个符号 ＝＝ 这是我们想要
看两者是不是相同时用的 这有点烦人
但是这个的效率有低
我必须声明变量
然后再给它值 这个有点烦
所以C语言允许我们简化这一步骤
你可以在左边声明变量
然后在右边赋值 只要用等号
在中间连接就行
所以其实 这两者是一样的 但是说实话
习惯用这一个还是比较好 因为它
代码少 易读 而且
让你的代码更紧凑
现在 对于循环 变量 条件
布尔表达式 不管从技术上还是概念上 有什么问题么?
好的
下面这一个要更有意思些
这是我上次用的例子
将Scratch与C语言相对应
一个函数
用普通人的话来说 函数是什么?
回答问题的时候要大胆些
函数是什么?
它会做某事
什么?
它会做某事
做某事
好的
我们就从这入手
所以函数会做某事
所以它是一些代码 最终会被在某处运行
然后能做某事
那么 我们对函数感兴趣 是因为它可以接受input
然后产生output
让我们来看一下这个
我们背后团队的成员前一秒刚出现了一下
这里我们有个桌子
但假设它代表一个大盒子
所以这是所谓的黑盒
一般来说 在设计 在计算机科学里 黑盒指的是
某人设计的一些功能
而你并不知道或在乎 这是怎么设计出来的
你只关心 这个黑盒 我们现在叫它函数
它会做某事
例如 如果台子上的这个黑盒代表printf
一个函数 从前面的例子 我知道printf
接受一个或多个参数 而第一个参数应该是一个字串
像是"hello, world" 如果我是在编程的那个人
而我想用printf 我可能会 像这样 拿一张白纸
然后在上面用黑笔写字
我把world 拼错了
H-E-L-L-O
所以我拿着黑笔 用最大最清晰的字
在纸上写下 "hello, world," 我说这就是我的参数
这是一个用一张白纸表示的字串
而现在函数printf的input就会是这个
所以我召出pirnft 将参数变为它的input
现在 我不知道很多年前编写出printf的那个人是怎样
但是我知道 根据记录 它存在的目的就是把我提供的input
输出出来
所以 虽然它的背后设计我并不知道
现在我可以看到 成功了
它在屏幕上输出了点东西
而现在 程序的控制权 如果有多行代码的话
就会回到我手上
所以虽然Colton
保佑他 正躲在你们面前的桌子下
就表示 我并不知道 也并不关心
printf是怎样实现的
我只知道 根据记录 它能做什么
以及我该怎样用它
现在 记得printf可以变得更复杂些
我们是在讲和Scratch中的Say拼图块相同的
上一次我也这么做了
我想要让我的hello程序 更多元些
而不是仅仅是个在程序中 加入"world"这样的硬编码
更不是D-A-V-I-D这样主观的东西
我想要问用户要他或她的名字
然后用他们提供的字串做点什么
所以现在 和刚才的printf 略有不同
刚才的printf 确实 做了某事 但它并没有把任何东西返回给我
对不对?
Colton没有递给我任何东西 一张纸也没有
发生的只是一个副作用
我把 "hello, world" 当作参数给Colton
结果是副作用得到的一个词 一些词 出现在了屏幕上
不过 getstring 就不一样了 getstring还是
一个函数 但是它能返回一些值
它不是只有一个副作用
它是真的能给我 那个在使用这个函数的人
一些东西
所以在这里要用 getstring 我们这儿写了
GetString ()
那么getstring 有接受任何参数或input么
不 看起来没有
它存在的意思是 获取一个字串
其他的就都不需要
那就让我再一次 假设这个黑盒不是printf
而是getstring 并且让我 在编程的这个人 召出或者使用
getstring 也就是写下 G-E-T-S-T-R-I-N-G ()
获取字串
我现在一点也不知道CS50的工作人员怎么设计出getstring的 但我知道
如果我等得够久 它就会做它该做的事
也许是用了变量 也许是用了条件 也许是用了循环
也许是用了函数 也许是用了 --
我只是在拖延时间 -- 也许用是其他的编程功能
但只要我等得够久 在现实中 电脑里
这个其实速度很快
如果我等得够久 getstring这个函数就会从用户手中
获取一个字串 这应该是用户用键盘输入的
然后 当getstring从用户手中 获取完这些字符
并把它们储存好之后 getstring这个函数
就会给我准备好output 我可以通过
赋值运算符得到这个output
如果我看一下这个输出 Obosi
在不知情的情况下 倾情参与 在变量里写下了
他的名字 代表了一个字串
赋值运算符表示 虽然听起来有点累赘
实际上 我确实需要自己来复制一份这个
因为在左边 哎哟
注意 在左手边我有 字串名字
所以我要自己来复制一份
其实这是个善意的谎言 因为在一两周内
我们就会发现字串其实并不是它们看起来这个样子
但现在 这是返回来的值
这是我在用了赋值运算符后 自己得到的一份
那么下面我该怎么做?
我们看到了两行代码中的第二行
所以我现在想要用printf
让我们再一次假装这个黑盒子是printf
而不是getstring
这一次printf有多少个参数?
看上去是2个
这里有2个逗号 但是其中的一个逗号
是在引号里面的
所以第一个参数其实就是这个
H-E-L-L-O 逗号 %s \n
现在我向printf提供了 不是一个 而是两个参数
那么printf 在我把这些input当作参数给它后
会怎么做呢?
它应该要接受第二个 也就是name
所以我刚才写的第二张纸叫name
它会把变量中的值放到占位符%s里去
所以 又一次 我们马上会看到
用printf的副作用 也就是我们现在看到的 不是 "hello, world,"
而是 "hello, Obosi."
请掌声感谢我们的志愿者
他们中只有一个预先知道这些
好的
也许这看上去很简单
如果你已经比较熟悉这些的话 但 希望你会永远记得
函数运作时是什么样子
所以 我们不只有getstring
在CS50库里 还有一堆函数
都用大写标出 表示是我们写的
一般 在C语言中 你们用的其他函数都是小写的
但我们故意用了大写字母 表示这些只是
在这一两周用来给你们练习的
它们简化了从用户那儿得到input的过程
CS50库里 并没有什么
是你用C语言 照着教科书写不出来的
但是 再一次 我们用这些只是为了让你们在这一两周练习
这样我们就可以抛去复杂性 让你们懂得
自己其实在做的 就是简单地
从用户手中拿input
所以你们也可以用 GetChar GetDouble
双精度浮点数
然后还有GetFloat
什么是浮点数?
我们就从这开始
[学生回答]
没错
它是一个数 带着小数点的数
所以int表示整数 就是0到9之间的数字
的重复 而float(浮点数)
是有小数点的数
一个双精度浮点数呢 也带小数点
但是小数点后的数字会更多
我们过一会会再讲到这个
但一般来说 我们的库可以返回给你们的
像这样的每一种数据类型 变量类型
都用不同长度的比特来储存的
一般 一个Char 它就表示一个字符 用8位
这实际上和我们上周让志愿者
上台来表现Ascii 字符的时候 是符合的
所以一个Char有8位
一个浮点数呢 一般有32位
而一个双精度浮点数 你们大概猜到了 有64位 也就是更多的数字
这也就是说你可以表示更大的数 或更精确地表示数字
不过 我们后面还会再讲到它
GetLongLong 现在看上去有个很蠢的名字
只是一个比一般整数大一倍 长一倍的整数
它有64位 而不是32位
而GetString 我们已经一直在用了
不过我们发现 在CS50库里
它们是以两个文件的形式存在的 一个叫cs50.h
这里面还有另外两种数据类型
布尔表达式在C语言中不存在 你可以在程序中
用0与1们来模拟它
但我们在CS50库里创造了"true"与"false"符号
来表示1与0 这样你们就不用自己
来用1与0来硬编码了
不过我们在后面还会看到它们
字串 一样 也不存在
这也是为什么我说 这现在是一个善意的谎言
不过我们不久就会来揭开它的真面目
不过现在 一个字串是一些字符组成的序列
在C语言中 我们确实有不同的数据类型
注意 这些都是小写的
所以你们刚才看到的函数都是CS50写好的
会把值返回给你 而这个值属于这其中的某个类型
好 这里的小抄呢 只是让大家先了解一下
printf不只会把%s当然占位符
它还会把%d作为十进制整数常数 或者%i也行
%f是浮点数 %c是字元 如果你只是想
把一个字符加入一个格式已定的字串里 就像我们一直在做的那样 你可以用%c
然后 这个有点烦人 %lld是一个很长 很长的十进制整数常数
这只表示 如果你需要一个很大的数
然后你在用一个叫 long long的东西 我们在练习中还会看到
你可以用%lld告诉printf在这里插入一个很大的整数
经由它的第二或者其他的参数
最后 我保证printf还支持
一些其他的转义序列
我们见过是\n
\r 你们可以看到
是个老派的东西
如果你用过真的打字机 拉过曲柄的话
它不只会跳到下一行 还会
从右移至最左边 那 \r
也会把你的光标移回行首
而不会把它向下移
但 以后 我们还会再讲
\",\"",\\
是解决我先前提到的谜语的答案
\0实际上挺有意思
不过我们过一会会再来看
所以现在 让我们来看CS50工具
让我们用先前的例子来做个暖身
然后来来看更复杂的事情
所以 我打开了gedit这个程序
这是我的图片编辑器
我可以做这个
让我来关掉这个窗口
按这个图标 在gedit左下角的这个
靠近菜单的 就可以打开gedit
让我来把这个例子保存到
John Harvard的文件夹
John Harvard的文件夹只是它的目录
他的文件默认保存到这里
我会把这个命名为hello-0.c
我选这个名字 是想要它和课程网站上的代码
和YouTube的视频相符合
现在 我要开始写我的程序了
让我放大下让大家看得清楚
让我来写下 int main void
就像黄色拼图块一样 会开始运行这个程序
我多年养成的习惯 是打下{}
然后回到我想写下代码的地方
只因为这样会让一切平衡得好
特别是当程序写很长的时候
现在 在这里 我会写 printf
"hello, world \n ");
我只是在重复我们到现在为止当作理所当然的而已
现在我把它缩小
底下的这个终端窗口 我可以
在这个黑白窗口里做什么呢?
我可以用它做什么呢?
在这里我可以运行命令 也可以编译东西
我会让一切保持简洁
我要用一个叫Make的程序
它严格意义上来说并不是一个编译器
编译器叫做Clang 不过我们后面会再来说
大概在一两周之后
同在 我会输入 make hello-0 但是你们当中
那些自己在头脑里比对我输入的和我应该输入的人
会知道 我做错了什么
很明显 这里有些问题
在我去看错误是什么之前 有人知道我做错了什么么?
[学生回答]
没错
我没有输入库的头文件
这些以.h结尾的文件都可以叫做头文件
它们共同属于"库"
库是别人写下的代码
所以标准的库是
一批里面包含了其他人写下的代码的文件
所以这个我忘了加
那为什么我这出了错呢?
让我在终端窗口这里往上拉一点
不幸的是 在C语言 和其他很多编程语言里
特别是在对你们来说新遇到的语言里 错误信息很准确
但也很难懂
这里的错误 用红字标出的 是
"库函数printf类型不明" 然后看下一行 "int const char... *
这很快就让人糊涂了
但是你们应该开始做的 特别是如果是新遇到这问题
是去找关键词
显然 我所看到的一半文字 我都不懂
不过你们 在一周内 就会懂
但我看到了printf
不久 这就应该能唤起你们的记忆了 好的 printf
printf这有什么出问题了
是我拼错了么?
不是 看上去不是 哦
在我教编译器说 它存在前 我不能使用它
所以 再一次 相信你的直觉 即使你还
不懂错误信息是什么
而且这里 解决办法就是把它加到文件前头去
像这样 用control-s 或者文件菜单重新保存
现在如果我回到这里 我要清空这个
control-L是一个简便的清空屏幕的方法
然后我会输入 make hello-0 回车
现在我看到了一堆难懂的符号 不过我们后面会再说
这是Make帮你的结果
它会自动 用Clang 也就是编译器
接受一个很讨人厌的命令
但事实上 我没有得到任何报错 就意味着这应该成功了
那么现在我要 - 让我再放大一次
./hello-0 回车 的确 我看到了"hello, world."
那让我们再稍微加强一下 再重复一次步骤
我要用另存为将它重新命名为 hello1.c.
现在我要声明一个变量叫name 所以是 string name
然后我要赋它一个值
也就是D-A-V-I-D ";
然后我要把world替换成字串 要用什么占位筌符?
%s
现在 printf有多少个参数?
两个
那我就到引号外去
我在逗号后输入 "name"
但这次 我又犯了另外的错误
但就假装我没注意到它
让我们到这里
注意 我输入"make hello 0" 这个输得有点烦了
要打make还有那么一长串
结果 在Linux里 你可以在键盘上敲击向上键
然后就可以滚动浏览
我前面到现在执行过的所有命令了
所以如果我 向上 向上 make hello 0在这里
我不想要它
我想要把它改成hello 1 回车
最终 它会帮你省些时间
好的
不幸的是 这里有错误
所以让我往上滚动一点
看上去我真的把程序弄坏了
我的意思是 天 只有两行代码 但有10行错误
不过我们先来看最上面
使用未声明标识符
我是想说 标准 i n吗?
不是
我是想说字串
但是声明的字串变量类型在哪里呢
在CS50库里
所以在前几周里
用C语言里有的还不够
我还要再到上面这来 我可以把它放在上面或下面
但我就按字母顺序排好了
我要把cs50.h包括进来
它已经在你们的CS50工具里了
它是开源的 所以在网络上的人
也可以在自己的电脑上使用
但它是包括在CS50工具里的
现在 让我回去重新用make hello 1编译
糟糕
还是有个错误
让我滚动到第一个来
不过这个有点复杂
多字符常数
这个对我没有帮助
但是注意 Clang至少要好点
它用一个绿色脱字号表示 这里我做错了
为什么绿色小箭头指向的
是我名字旁边的单引号?
这是个你们得学会习惯的事
特别是如果你用Python 或 Java Script或其他语言编过程
在那里 这样的细节不重要
在但C语言里 它很重要
如果你在声明一个字串 这个字串是0或更多字符组成的序列
你必须用双引号
所以我要把这里改成一对
双引号
单引号也是有用的 但只有在你使用
单个字元时 这个我们会另找时间来讲
现在 双引号是必须的
所以现在 让我回到我的终端窗口 make hello 1
有谁有信心
这次程序会正确编译?
好的
有三个人
好的
回车
它真的成功了
这次没有错误出现
虽然程序变得更复杂了些
如果我现在输入 ./hello 1回车 它就会说"hello, David."
但让我们来做第三个版本
这里程序会真正多元化
让我把文件名换掉
这只是为了让文件名和你们后面在网上看到的
回车
现在我要在里面 不想存储硬编码"David"
我要怎样才能很方便地改进这个程序?
我可以用getstring
下面要发生什么可能不太明显
所以我实际上要加一行 printf 然后 name:"
只是要在屏幕上给用户一个提示
现在我要到这里 我要用键盘上的快捷键
我要向上 向上 然后把hello1 改成 hello2 回车
谢天谢地 我有取得进展
然后我会到./hello这里
把它变成2 回车
现在我的程序
我会放大它 -- 变得漂亮些了
名字这次就 说是Rob好了 回车 hello Rob
我们可以再做一次
名字 Lauren 回车
名字 Joseph 回车
名字 让我们故意弄难搞一些 回车
呃
这倒不是一个bug
只是长得不太好看
我们在未来可能可以解决这个问题 不过不是现在
不过凭直觉 你们会怎样
处理这个挑战呢?
它只是看上去有点蠢
那怎样不要它看上去有点蠢呢?
我们可以 好的 我听到了几个条件 条件 还有循环
我们可以用 一、一个条件 去检查
用户给我们的字串的长度
如果是0 那就只是说 他们只是敲了回车
那也许我应该冲他们喊然后再提示他们做一次
但我要怎样再提示他们一次?
我还听到有人说循环 我可以一遍又一遍
去提示用户做相同的事
让我们用CS50库里的另一个函数
来做例子
让我把这个文件关了
来创建一个新的
我要叫它 adder.c 因为这样做简单算法更方便
虽然这对于任何一台现代电脑来说
根本不算什么
不过让我 --
从上次我吸取了教训 -- 加上cs50.h, stdio.h,
int main void 现在 我只是盲目相信
但就让我们相信 不久我们会理解这些是什么意思
然后我会说 "给我一个整数"
那现在 我要怎样得到一个整数?
最终 我想声明一个叫x的变量 它的类型是整数
然后把它储存在用户提供的整数里
这个真是有点复杂
不过如果有人想提议 我该怎样声明一个叫x的整数呢?
int x.
就是这么简单
给我一个整数
叫它x
现在我要用赋值运算符
我要怎样把左边用户给的值储存起来?
显然 我不想用getstring 我要用getint
有参数么?
没有
所以是()
然后用;结束这一行
让我再做一次
给我另一个整数
这一次 我把这个整数叫y = getint
下面让我做一件特别简单的事 像数学里一样
printf %d的总和 %d是一个整数的占位符
%d is %d.\n
好的
事实上这不是数学
但是如果我想要说 这个值的总和加上这个值等于另外这个值
我一共要给printf多少个参数?
一共要多少个?
4个 对不对?
这个字串 然后3个值
x是我第一个想要放到%d里的
y是第二个
然后我现在想说z 但是z并不存在
但这不重要
因为你本能就会
特别是如果你有一个绘图计算器的话 会在里面输入什么?
x加y怎么样?
所以这并不是一个真正的变量
它是两个变量的和
这完全合理
C语言当然会理解这么简单的算法
; 保存
现在让我到下面 输入make adder 回车
没有错误 这也是一项进展
输入adder
另一个键盘快捷键
如果你对这么多命令感到厌烦的话
如果你开始输入一个命令 像是./ad
在这你觉得有烦 你可以敲tab键
然后让电脑帮你输入完 如果你知道ad之后的东西是什么的话
所以让我按回车键
给我一个整数 1 2 谢天谢地 是3
但同样 测试程序
不能只试一次
让我来试一个特例 像是-1 给我1
这一个也成功了
我应该再做些测试
但我对它现在的状况有信心
现在 让我们再来试另一个程序
里面用了不同的句法
让我来建一个新文件
我叫它 conditions0.c 和网上的代码范例相统一
让我加上cs50.h
stdio.h, int main void
好的
这下行了
我们有了自己的标准样版
这一次 我要对printf说 我想要一个整数
只是为了让这个提示看上去更亲切些
现在我想要从用户手上得到一个整数
这一次我要叫它n 因为n听上去像数字(number)
getint 现在 我想要做什么呢?
如果n是 -- 我要放大一下 --
如果n比0大 我想要做以下事
printf “你选择了一个正数” 否则 我会在printf里键入
"你选择了一个负数"
好的
这个程序 虽然我很快写出了它 从句法上讲 看上去是对的
让我们试一下
Make condition 0
看上去运行成功了
Condition 0 回车
让我们给它50这个整数
我选了一个正数
再试一次
Condition 0
-50
我选了一个负数
但现在 让我们选一个特别的
这个例子可能会给你们带来想像不到的麻烦 0
我很确定 这是个特例
因为0既不是正数也不是负数 我的程序虽然句法上是正确的
编译了 也运行了 逻辑上来说 是不对的
这里简单的解决办法 让我能发现问题
让我能解决 n=0 的问题的是什么呢?
如果n=- 那么我想对printf说 "你选择了0"
让我来试一下
让我回到这里 清除窗口 然后重新编译
嗯
出现了一个错误
但我是想要检查n是不是等于0
所以又一次 我们要适应这个
等于号是赋值运算符
所以这里有个错误 虽然编译器不让我犯错
但是我们实际上是把0复制到了 n里
而这不是我想要的
看是不是等于的话 要用＝＝
所以这大概是更好的解决方法
让我重新把它保存成 condition 1 它是一个新的 改进后的版本了
如果我现在重新编译它 make conditions
-- 哎哟 -- make conditions1./conditions1 回车
"请给我一个整数" 我来输入50
它还是好的
-50 好的
0 它确实探到我选择了0
那现在我还可以做什么呢?
当然 我们可以做更复杂的事
但我提议 就到此为止
让我们看这里 这可能是部你们最喜欢的卡通
但今天以前你们可能都看不懂
CS50课上的笑话差不多就是这个水准了
好的
但是更重要的 是我想我应该给周一留个悬念
所以我们开始用整数了
我们提到了浮点数
我们还提了双精度浮点数 它 也一样
在小数点后有数字
但是 浮点数和双精度浮点数 以及电脑
从本质上来说 都不能准确地表达某些数值
从数学课上 我们知道 在小数点后
你可以放任意长的数字
你还可以用一个|表示 小数点后有无限个数
不幸的事 在电脑里 你不能这么做
那么 特例就来了
比如说 假设你在做有关金融的事
你在计算百分比
涉及到元 与分
而这些几分钱并不是整的
我们发现 不是整的几分钱
放在电脑系统里的话 会让聪明的人
有机可乘
对于你们中还没有看过的人 我想花30秒提下
一个很好的电影 叫Office Space 它就讲了一个
我们在周一一开始就要解决的问题
如果我们能把音量调大些
我给你们看30秒的Office Space
[视频播放]
我说 你都一直没上班 你要保住你的工作
事实上 我被升职了
我可以写出一个病毒 让这里遭受巨大损失
那 是怎样做到的?
每一次银行交易 计算利息时
这样的交易每天有几千笔 电脑会算出不是整的几分钱
我才不要做违法的事
违法?
Samir 这是美国
我们得对上帝发誓的
如果除了我们没人知道这事
家庭成员 女朋友 都不行
当然
同意
别担心伙计
我也不会告诉任何人的
[视频播放结束]
好 这是CS50 刚才的是Office Space
在周一 你们就会理解这一切了
下周见
在下一周的CS50里
Rob要学会适应不再在食堂吃东西的生活