
视频元数据
- 视频标题:哈佛大学【中英⚡计算机科学导论(2025)|CS50x Introduction to Computer Science】 p03 P3 第1讲 - C
- 时长:约1小时(CS50x 2025第1讲)
内容概览
本讲介绍CS50x 2025中的C编程语言。内容涵盖从Scratch到C的过渡、VS Code云编程环境、编写和编译你的第一个C程序、变量、数据类型、条件、循环、自定义函数,以及整数溢出和浮点数不精确性等现实世界中的bug。
1. 引言:从Scratch到C [00:00]
- 教授从Scratch(可视化编程)过渡到C(基于文本的编程)。
- 核心思想:编程是关于输入变成输出的;无论使用何种语言,解决问题始终是核心范式。
- 源代码是程序员编写的内容——它看起来像英语,但并不是英语;它必须精确且无歧义。
考点:源代码与机器码 — 人类使用源代码(可读文本)。计算机使用机器码(0和1)。一个编译器 (compiler) 将源代码转换为机器码。
2. 编程环境:VS Code 与 CS50.dev [04:57]
- CS50.dev 是CS50的云端编程环境;它运行着 Visual Studio Code (VS Code),这是一款行业标准工具。
- 需要一个免费的 GitHub账户 来登录。
- VS Code的功能:
- 右上角:代码标签页(类似于浏览器标签页),你在这里编写代码;它会自动为语法着色。
- 底部:终端窗口——一个命令行界面 (CLI),你在这里输入文本命令而不是点击图标。
- 左上角:文件资源管理器(GUI),以可视化方式显示文件和文件夹。
- 活动栏:功能图标,包括 CS50的鸭子——一个内置的问答聊天机器人。
理解:There are two ways to interact with the computer: GUI (graphical user interface) via clicks, and CLI (command line interface) via typed commands. Programmers often prefer CLI for power and efficiency, though VS Code offers both. (理解:与计算机交互有两种方式:通过点击的GUI(图形用户界面),以及通过键入命令的CLI(命令行界面)。尽管VS Code两者都提供,但程序员通常更偏好CLI,因为它更强大、更高效。)
3. 在C语言中编写和运行 Hello World [09:00]
- 创建C文件的步骤:
- 在终端中输入
code hello.c来创建一个新文件。 - 在编辑器标签页中编写程序代码。
- 在终端中输入
- 文件命名:约定是
something.c;使用全小写,无空格。 - 经典的
hello.c程序:
#include <stdio.h>
int main(void)
{
printf("hello, world\n");
}
- 理解:
int main(void)目前可以“暂时不深究”——就像Scratch中的“当绿旗被点击”积木一样。
4. 编译和运行:make 与 ./hello [12:00]
- 要编译程序:输入
make hello(make很智能——它会自动找到hello.c;不要输入make hello.c)。 - 要运行程序:输入
./hello(点斜杠表示“在当前文件夹中”)。 - C程序的三个步骤:
- 创建文件(
code hello.c) - 编译它(
make hello)——将源代码转换为机器码 - 运行它(
./hello)
- 创建文件(
易错点:
./hello和hello— 如果你只输入hello,终端会显示“找不到命令”,因为计算机不知道去哪里找它。./明确告诉计算机“在此文件夹中运行hello”。
ls命令:列出当前文件夹中的文件;是“list”的缩写。绿色表示可执行文件。
5. 理解 printf 和转义序列 [15:30]
printf是C语言中对应Scratch的说 (say) 积木。- Scratch到C的映射:
- 函数名 = 函数名(例如,
printf) - 白色椭圆 = 圆括号
( ) - 输入/参数 = 圆括号内的值,用逗号分隔
- 双引号
" "是包裹字符串所必需的
- 函数名 = 函数名(例如,
- 反斜杠
\n是一个转义序列,表示“换行”(在屏幕上按回车)。- 如果没有
\n,终端提示符会紧挨着你的输出出现。
- 如果没有
易错点:换行不是自动的 — 你必须使用
\n明确告诉计算机换行。如果你想要两个空行,就使用两个\n,以此类推。
- 其他转义序列:
\r= 回车(将光标移动到行首,就像老式打字机一样)\"= 打印一个字面的双引号(因为引号在其他情况下是特殊字符)\\= 打印一个字面的反斜杠
- 思考:就像数学中的
f(x)记号一样,C语言中的函数接收输入(参数)并产生输出。printf的输出被称为副作用 (side effect)——即发生在屏幕上的事情。
6. 头文件和库:stdio.h 与 cs50.h [20:00]
#include <stdio.h>是一个头文件(任何以.h结尾的文件)。- 包含C语言本身附带的功能。
printf函数在此声明;如果没有这个包含,编译器会报错“未声明的库函数 printf”。
易错点:
stdio.h而非studio.h— 每年都有一些学生会犯这个拼写错误。是STDIO(标准输入/输出)。
- 库 (Library) = 别人为你编写的代码集合(MIT、C语言作者、CS50团队)。通过包含正确的头文件,你就可以使用那些函数。
- CS50的库 (
cs50.h) 提供了更友好的用户输入函数:get_string("提示")— 从用户获取文本get_int("提示")— 从用户获取一个整数get_char("提示")— 获取一个字符get_float("提示")— 获取一个带小数的实数
-
理解:在原生C语言中获取用户输入出奇地困难。CS50库是“辅助轮”,几周后将被移除。
-
手册页:现实世界中的C文档晦涩难懂;CS50在 manual.cs50.io 上提供了一个更友好的版本供参考。
7. 变量、数据类型和获取用户输入 [25:00]
- 变量 (Variable):存储值的容器,使用赋值运算符
=(从右到左:右边的值被复制到左边的变量中)。 - C语言要求你声明每个变量的类型:
string— 文本(CS50类型;非原生C语言)int— 整数(例如,1, -5, 0)char— 单个字符(例如,'y','n')float/double— 带小数点的实数long— 更大的整数(64位)
- 声明和赋值的示例:
string answer = get_string("你叫什么名字? ");
易错点:分号 — 在C语言中,每条语句必须以
;(分号)结束,这与Scratch中积木自成一体不同。忘记分号是最常见的初学者错误之一。
8. 格式代码和占位符:%s, %i, %f [28:00]
printf使用占位符(格式代码)将值插入字符串:%s— 字符串占位符%i— 整数占位符%f— 浮点数占位符%li— 长整数占位符
- 与Scratch的
join积木拼接字符串不同,C语言使用printf留下一个带占位符的“模板”:printf("hello, %s\n", answer); - 函数的多个输入用逗号分隔。
- 理解:这里有一个微妙的区别——引号内部的逗号是用户看到的字面英语逗号;引号外部的逗号是C语言中函数参数之间的分隔符。
9. 条件语句:if, else if, else [32:00]
- 布尔表达式:答案为真或假的问题,例如,
x < y,x > y,x == y。 - 运算符:
<,>,<=,>=,==(相等),!=(不等)。==(双等号)是相等性测试;单个=是赋值。这是一个经典的初学者陷阱。
易错点:
==与=— 在C语言中,=是赋值(例如,x = 5),而==是测试两个值是否相等(例如,if (x == 5))。在if语句中使用单个=会导致bug。
- 条件结构:
if (x < y) { printf("x 小于 y\n"); } else if (x > y) { printf("x 大于 y\n"); } else { printf("x 等于 y\n"); } -
思考(设计):在这个三路分支中,第三种情况
x == y是不必要的——如果x不小于y且不大于y,逻辑上它必然等于y。良好的设计意味着不让计算机做不必要的工作。 -
逻辑运算符:
||= 或(两个竖线)&&= 且(两个与号)!= 非(感叹号)
- 不区分大小写输入的改进示例
if (c == 'y' || c == 'Y')— 避免重复自己(DRY原则:不要重复自己)。
10. 循环:while, for 和 do-while [40:00]
- 为什么需要循环? 复制粘贴代码是一种设计缺陷。如果你发现自己重复编写代码,使用循环通常会更好。
- while 循环:
int i = 3; while (i > 0) { printf("meow\n"); i--; } - for 循环(更常规):
for (int i = 0; i < 3; i++) { printf("meow\n"); } - 计数约定:计算机科学家从 0 开始计数,并数到“小于但不包括”目标值:
i < 3。 - 无限循环:
while (true) { printf("meow\n"); }— 将永远运行下去。- 逃生技巧:按 Ctrl+C 中断失控的程序。
易错点:for 循环语法 — 在for循环中使用分号(
;)而不是逗号来分隔各部分。不要在for(...)后面加分号。
- do-while 循环 — 至少执行一次循环体,然后检查条件:
int n; do { n = get_int("数字:"); } while (n < 1); - 理解:为什么用do-while而不用while?因为我们想至少提示用户一次;普通的while循环会在
n有值之前就检查n < 1。
11. 自定义函数和作用域 [47:00]
- 在C语言中定义你自己的函数(例如,一个
meow函数):void meow(void) { printf("meow\n"); }- 第一个
void= 无返回值(无输出) - 第二个
void= 无参数(无输入)
- 第一个
- 带参数的函数:
void meow(int n)— 接收一个整数输入。 - 带返回值的函数:
int get_positive_int(void)— 使用return关键字返回一个整数。 - 函数原型 (Function prototype):要使用在文件后面定义的函数,请在顶部(
main之前)声明它的第一行,以便编译器知道它存在:void meow(int n); // 顶部的原型
易错点:函数顺序 — C语言自上而下读取代码。如果你在定义
meow()之前调用它,编译器会报错“调用未声明的函数”。解决方案:将函数的第一行(其原型)复制到文件顶部。
- 作用域 (Scope):变量只存在于定义它们的花括号
{ }内。- 在
main中定义的n在meow内部不存在;你必须将它作为参数传递。 - 理解:这就是为什么输入和输出很重要——它们是函数在不“共享”变量的情况下进行通信的方式。
- 在
12. Mario问题:嵌套循环和抽象 [52:00]
- 受超级马里奥兄弟启发的编程问题——使用
printf打印砖块/问号。 - 水平行,共4个问号:
for (int i = 0; i < 4; i++) { printf("?"); } printf("\n"); // 循环之后换行 - 垂直列,共3个砖块:在循环内部使用
printf("#\n")。 - 网格(3x3砖块):嵌套循环——外层循环用于行,内层循环用于列:
for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { printf("#"); } printf("\n"); // 每行之后换行 }
易错点:换行的位置 — 对于一行,将
\n放在内层循环之后(每行一次),而不是在内层循环内部(否则会变成垂直的一列)。
- 抽象策略:分解问题——先编写一个
print_row(int n)函数,然后在main的循环中调用它。 - 常量:
const int n = 3;— 一个不能被改变的变量(防止你自己或同事意外修改它)。 - 魔法数字 (Magic numbers):在多个地方重复的硬编码数字是不良设计;使用
const变量或向用户询问。
13. 算术、整数溢出和浮点数不精确性 [59:00]
基本算术运算符
+加法,-减法,*乘法,/除法,%取余(模运算)
整数溢出
- 问题:计算机以固定位数的二进制存储整数(
int通常为32位)。- 32位可以表示从0到约40亿(无符号),或大约-20亿到+20亿(有符号)。
- 如果你不断将一个数翻倍(指数增长),它最终会超出这个限制 → 数字会回绕为负数,然后变为零。
- 推导:如果所有32位都是1,再加上1,没有第33位来“进位”——所有位都重置为0,得到0。
考点:现实世界的例子——波音787 — 一个使用32位整数以十分之一秒为单位计数的软件计数器在持续通电248天后溢出,导致电力中断。解决方案:定期重启飞机!
截断和类型转换
- 整数除法会截断:
1 / 3 = 0(而不是0.333...),3 / 2 = 1(而不是1.5)。 - 修复:使用浮点数或强制转换值:
(float) x / y— 告诉计算机将这个整数视为浮点数。
浮点数不精确性
- 浮点数也使用有限位数(32),双精度浮点数使用64位——两者都是有限的。
- 实数有无穷多个,所以计算机无法完美地表示它们全部。
- 示例:用50位数字打印
1/3得到0.3333333333334326744407...— 超过某一位后就不准确了!
2038年问题
- 计算机一直使用32位整数来统计自1970年1月1日以来的秒数(Unix纪元)。
- 在2038年1月19日,这个计数器将溢出,可能导致“世界末日”——时钟重置到1901年或变为负数。
- 思考:与Y2K问题相同的基本问题——有限的二进制位无法表示无限的量。
14. 总结:正确性、设计和风格 [1:10:00]
- 评估代码质量的三个维度:
- 正确性 (Correctness) — 代码是否做了它应该做的事?(由
check50检查) - 设计 (Design) — 代码写得有多好?是否高效、逻辑清晰、没有做不必要的工作?(由
design50鸭子聊天机器人检查) - 风格 (Style) — 代码是否美观可读、缩进正确?(由
style50按钮检查)
- 正确性 (Correctness) — 代码是否做了它应该做的事?(由
- VS Code中的工具:
check50,style50,design50内置于CS50的环境中。 - 理解:这些工具不仅仅用于评分——在现实世界中,工程师也以类似的方式编写测试并审查代码质量。
关键词/标签
C语言 CS50 哈佛 编程 源代码 编译器 VS Code 终端 CLI printf 转义序列 数据类型 变量 条件语句 循环 函数 作用域 整数溢出 浮点数不精确 Mario问题 解决问题
生成时间:2026-09-11 15:18:30 生成平台:VideoNote www.xygy.top