跳转到内容

第一章 绪论

  1. 程序设计的目的和应用场景
  2. 计算机的定义与通用计算机的特点
  3. 冯诺依曼架构的组成与关键概念
  4. 控制器的组成与工作流程(取指-解码-执行)
  5. 存储单位与编址(bit → byte → word,及字长 word size)
  6. 运算器(执行单元)的组成(ALU + 寄存器组/数据寄存器 + 标志等)
  7. 高级语言 vs 低级语言的区别
  8. 语法(syntax)与语义(semantics)的概念
  9. 编译执行 vs 解释执行的区别
  10. 算法的基本要求与好的算法设计标准

  • 程序设计的目的:设计出让计算机完成某个任务的程序,适合去完成大量能被规范化的重复操作。 [来源:第1章 p4,为什么要学习程序设计?]
  • 设备控制:通过程序控制各部件按一定规则运转,协调不同部件之间的工作(如PECVD反应系统)。 [来源:第1章 p5,Notes]
  • 数值模拟:准确模拟各种反应条件,帮助研究人员弄清微观反应机制(如分子动力学模拟)。 [来源:第1章 p7,Notes]
  • CS(计算机科学)vs CE(计算机工程):
    • 相同点:都需要大量编程培训
    • 不同点:CS研究"哪些问题是可以计算的?如果可以,有多困难?";CE研究"实际硬件怎么配合计算需求?" [来源:第1章 p8]

  • 计算机定义:一个由程序操控的,可用于存储和处理信息的机器。 [来源:第1章 p10]
  • 通用计算机:可以加载不同的程序来完成不同任务。 [来源:第1章 p10]
  • 计算机两个关键点:
    1. 计算机是用来处理和存储信息的
    2. 计算机是由可更改的程序控制的 [来源:第1章 p10]

1.3 冯诺依曼架构(重点)

跳转到:1.3 冯诺依曼架构(重点)
  • 冯诺依曼架构五部分组成:
    1. 输入设备
    2. 输出设备
    3. 存储器
    4. 算术逻辑单元(ALU)
    5. 控制单元 [来源:第1章 p11,冯诺依曼架构]

规则/规范(三个关键概念)

跳转到:规则/规范(三个关键概念)
  1. 指令和数据共享同一存储器/统一地址空间(“存储程序”思想;工程实现中可能存在指令/数据缓存分离)
  2. 存储器单元可以被寻址,而不管里面存放什么类型的数据
  3. 程序在抽象层面上通常按指令序列逐条执行,但会因分支/跳转/调用/中断等发生控制流改变(并非永远自上而下) [来源:第1章 p11]
  • 冯诺依曼架构 vs 哈佛架构:
    • 冯诺依曼:数据和指令存储在一起,共享总线
    • 哈佛:数据和指令分开存储,有各自的总线,能同时读取指令和数据
    • 哈佛/改进哈佛结构常见于微控制器、DSP等;很多现代处理器(含大量ARM内核)在缓存/总线层面采用“改进哈佛”(I-cache/D-cache分离),但在编程模型上通常仍呈现统一地址空间 [来源:第1章 p11,Notes]

  • 存储器功能:保存正在运行的程序代码和数据 [来源:第1章 p13]
  • 存储单位层次:

    • bit(位):内存的最小单元,存储一个二进制位
    • byte(字节):8个bit组成一个byte
    • word(字/字长):由若干个byte组成,表示处理器的自然数据宽度(word size)。常见为16/32/64位;并非“最大”,现代CPU还可能有更宽的向量寄存器/指令 [来源:第1章 p13]
  • 在多数通用计算机中,主存按字节编址,容量也按字节计量(也存在按字/半字编址等特例) [来源:第1章 p13]

  • 存储器分类:

    • 主存储器(内存)
    • 外存储器/辅存(SSD/HDD/闪存等) [来源:第1章 p13]
  • ⚠️ 断电后,**易失存储器(典型如DRAM)**中的数据会丢失(非易失存储器如ROM/Flash不会) [来源:第1章 p13]

  • 控制器功能:

    1. 指挥存储器和其它部件之间的信息的传送
    2. 通过系统总线与I/O接口协调CPU与输入/输出设备的数据与控制信号传送 [来源:第1章 p14]
  • 控制器包含:

    • 一个指令译码器:解析指令字段并生成/选择相应的控制信号(或微操作序列)
    • 若干控制寄存器(常见包括):
      • 指令寄存器(IR):保存当前正在译码/执行的指令
      • 程序计数器(PC):保存下一条将被取指的指令地址(顺序执行时通常自动递增,遇到跳转/中断时会被改写) [来源:第1章 p14]

执行逻辑(控制器工作三步)

跳转到:执行逻辑(控制器工作三步)
  1. 取指(fetch):按PC给出的地址从存储器取出指令送入IR,同时PC更新为下一条指令地址(顺序执行时通常为PC+指令长度)
  2. 解码指令:将指令解码成一系列的控制信号
  3. 执行指令:将控制信号发送给相关部件,执行相应的运算 [来源:第1章 p15]
  • ⚠️ 从CPU内部数据通路看,ALU主要对寄存器/内部通路上的操作数运算;内存数据通常需先通过**加载(load)进入寄存器,再参与运算;写回内存通过存储(store)**完成 [来源:第1章 p14,Notes]

  • 【整理自 PPT】类比理解:寄存器相当于"工位空间",存储器相当于"仓库" [来源:第1章 p14,Notes]


  • **运算器(执行单元)**通常由算术逻辑单元(ALU)、寄存器组(register file)、移位/乘除等专用部件以及标志寄存器等构成(教材也常抽象为“ALU+数据寄存器”) [来源:第1章 p16]
  • ALU特点:
    1. 可以执行算术运算和逻辑运算
    2. 具体执行何种运算由控制器发送的控制信号决定
    3. 通常以两个操作数为输入(也可能来自立即数、移位器、向量通道等,具体与ISA/微结构有关)
    4. 结果通常先落在寄存器/执行单元内部通路,并可能更新状态标志(flags);必要时再写回寄存器或存储器 [来源:第1章 p16]

  • 输入设备:将人能理解的符号转换成计算机能处理的符号(键盘、鼠标、光笔…) [来源:第1章 p17]
  • 输出设备:将计算机的输出转换成人能理解的输出(显示器、打印机、音响设备…) [来源:第1章 p17]

1.8 计算机程序(软件)

跳转到:1.8 计算机程序(软件)
  • 计算机程序:完成某一特定功能的一个逐步执行的指令序列 [来源:第1章 p18]
  • 程序分类:
    • 系统程序:控制和管理计算机系统,支持应用软件开发(编译器)和运行(操作系统)
    • 应用程序:为了支持某一应用而开发(如抖音、淘宝、微信) [来源:第1章 p18]
  • 程序执行方式(取指-执行周期):第一条指令被从存储器中取出送入CPU → CPU解码并执行该指令 → 下一条指令被取出、解码、执行 → …… → 直至程序的所有指令都被执行完毕 [来源:第1章 p18]
  • 编程:创造一个程序的过程 [来源:第1章 p18]

  • 语法(syntax):一个编程语言的每个结构都有确定的形式 [来源:第1章 p20]
  • 语义(semantics):一个编程语言的每个结构都有精确的意义 [来源:第1章 p20]
  • ⚠️ 自然语言存在不精确性和二义性问题(ambiguity problem),而编程语言需要用精确和确定的方式来描述问题 [来源:第1章 p20]

  • 低级语言:

    • 机器语言(machine language):计算机的"母语",计算机只能直接执行用机器语言写成的程序
      • 例如:字节序列 00000100 00000001(0x04 0x01)可表示8086的一条指令(如 ADD AL, 1);机器指令通常按字节序列编码,长度可变
    • 汇编语言(assembly language):将机器语言用助忆符表示
      • 例如:ADD AL, 1,需要汇编器处理后机器才懂 [来源:第1章 p21]
  • 高级语言:适合人用的语言

    • 例如:x = y + 1,需要编译器/解释器翻译后机器才懂
    • 代表:C/C++, Java, Python, R 等;SQL 属于声明式查询语言(更偏领域特定语言,和通用编程语言的分类口径略有不同) [来源:第1章 p21]
  • 低级语言/机器码与指令集架构(ISA)强绑定:同一二进制程序通常只能在兼容ISA的硬件上运行;在兼容硬件上结果一般一致(差异多来自未定义行为、浮点舍入、并发时序等细节) [来源:第1章 p21,Notes]
  • 高级语言通常具有更强的源代码可移植性:同一份源代码可在不同平台上用对应的编译器/解释器重新翻译运行;最终生成的二进制/字节码仍与目标平台或运行时相关 [来源:第1章 p21,Notes]

判定条件(高级语言 vs 低级语言选择)

跳转到:判定条件(高级语言 vs 低级语言选择)
比较项高级语言低级语言
执行效率取决于实现与优化(常见情况下可做到很高)通常更接近硬件、易获得极致性能
可移植性通常较好(跨平台生态完善)通常较差(与ISA/平台强绑定)
编程难易通常较易(抽象层次高)通常较难(需要理解硬件细节)
[来源:第1章 p23]
  • ⚠️ 低级语言往往用在特定的且资源比较受限的设备上,如实时系统、嵌入式系统 [来源:第1章 p23,Notes]

执行逻辑(程序设计四步流程)

跳转到:执行逻辑(程序设计四步流程)
  1. 算法设计:把实际问题转化成能用程序表达的问题(程序设计的灵魂)
  2. 编码:把转化好的问题完善成可编译的高级语言代码
  3. 编译:把高级代码转换成机器码
  4. 测试(调试):确保程序能够正常工作 [来源:第1章 p25]
  • 算法的基本要求(常见五条):

    1. 输入:有0个或多个输入
    2. 输出:至少有1个输出
    3. 确定性:表述清楚、明确,无二义性
    4. 有效性:每一步都切实可行、可在有限时间内完成
    5. 有限性:可在有限步骤后结束并得到结果 [来源:第1章 p25]
  • 好的算法设计标准:

    1. 鲁棒性:在合理/预期的输入范围内稳定工作,并对异常输入与边界情况有防护与提示
    2. 高效:步骤少,执行快
    3. 简洁易理解:表达清晰、实现尽量简洁(不等同于一味追求“代码越短越好”)
    4. 可维护:其他人维护和改写很方便 [来源:第1章 p25]

代码模式(答题/面试套路七步)

跳转到:代码模式(答题/面试套路七步)
  1. 听:认真听取问题描述,问清楚开放问题的细节
  2. 给例子:要全面,包括特例
  3. 简单方案:大致描述你要干什么
  4. 优化:哪些地方可以改进,提升可靠性、运行效率
  5. 详细描述:描述你最终选择的方案
  6. 实施:写代码,内容干净整洁
  7. 测试:检查代码,改错,案例测试 [来源:第1章 p26]

1.12 编译执行与解释执行

跳转到:1.12 编译执行与解释执行
  • 编译执行:将源程序全部翻译成机器语言的目标程序,然后再执行

    • 流程:源文件 → 编译器 → 目标文件 → 链接器(+其它obj文件) → 可执行文件 [来源:第1章 p28]
  • 解释执行:由解释器在运行时读取源代码或中间表示(如字节码),边分析/边执行;并不一定严格“一行一行”,很多实现会先编译成字节码再由虚拟机解释执行 [来源:第1章 p28]

  • 编译型实现通常先生成目标代码/可执行文件;对同一可执行文件的多次运行无需重复翻译,且编译器可进行较激进优化,因此运行效率通常更高(也取决于优化级别与运行环境) [来源:第1章 p28,Notes]

  • 解释型实现通常在运行时做更多解析/调度开销,因此单次运行往往比本地编译慢;但现代解释器也会做缓存、热点优化,甚至JIT编译(因此“不能优化”并不准确) [来源:第1章 p28,Notes]

  • 【整理自 PPT】类比理解:编译执行相当于"吃席"(菜端上来直接吃);解释执行相当于"吃火锅"(边煮边吃) [来源:第1章 p28,Notes]

  • 典型实现示例(注意:语言与实现方式不完全等同):

    • C/C++:常见为本地编译生成机器码
    • Python(CPython):会先编译为字节码,再由解释器/虚拟机执行;PyPy等实现还可能使用JIT [来源:第1章 p28,Notes]

1.13 流程图表达算法

跳转到:1.13 流程图表达算法

代码模式(流程图符号)

跳转到:代码模式(流程图符号)
符号含义
圆角矩形开始、结束
平行四边形输入、输出
菱形分支(判断)
方框处理(运算/操作)
[来源:第1章 p27]

  • 推荐IDE:
    • Visual Studio Code(个人电脑)
    • Code::Blocks 10.05(机房)
  • 作业系统:头歌
  • 要求:VS Code编写和测试,之后贴到头歌上 [来源:第1章 p29]

补充知识(拓展阅读)

跳转到:补充知识(拓展阅读)
  • 19世纪末,“bug”已被工程界用来指代技术故障/缺陷(常见资料会引用爱迪生等人的用法;这里更建议记住“bug=故障/缺陷”这一语义即可) [来源:第1章 p31]
  • 1947年9月9日,Harvard Mark II 计算机因继电器中夹入一只飞蛾而出现故障;团队将飞蛾取出并记录在日志中(这件事让“bug/调试”故事广为流传) [来源:第1章 p34]
  • 团队将飞蛾用胶带粘贴在日志本上并写下注释(常见引用为“First actual case of bug being found”);Grace Hopper 后来在演讲中传播了这一典故 [来源:第1章 p34]
  • Grace Hopper 是早期编译技术与 COBOL 推广的重要人物(COBOL由委员会制定,她是关键贡献者之一);英伟达(NVIDIA)“Hopper”架构以她命名 [来源:第1章 p34,Notes]