现在是2024年的2月13日,一月份购置了《软件设计师教程》一书,想着单单看着总会遗忘很多知识点,于是有了这一篇文章。原本的打算是做成零碎的文章,但零碎的知识点于平时别的碎碎念会产生混乱,于是今日将他们整合到一起,同时将之前写过的相关内容一并放于此地。这一篇文章会慢慢更新,逐步完善下去,希望不会再次发生弃坑的恶习吧~

[TOC]

计算机系统硬件的基本组成

​ 计算机系统是由 软件和硬件 构成的。硬件系统由 运算器、控制器、存储器、输入设备、输出设备 五大部件组成。运算器、控制器等部件被集成在一起统称为中央处理单元 Central Processing Unit 。输入和输出设备合称为外部设备。存储器分为内部存储器和外部存储器,内部存储器 速度高、容量小,一般用于临时存放程序、数据及中间结果,外部存储器 速度慢、容量大,长期保存程序和数据。

计算机系统

中央处理单元

​ 中央处理单元 CPU 是计算机系统的核心不见,它负责 获取 程序指令、对指令进行 译码 并进行 处理

CPU的功能

  • 程序控制 :控制程序执行的 顺序
  • 操作控制 :发出 操作信号 至对应部件并使对应部件执行程序
  • 时间控制 :控制操作信号的 出现 时间、保持 时间和出现的 时间顺序
  • 数据处理 :算术和 逻辑运算
  • 中断(异常)响应

CPU的组成

​ CPU主要由 运算器控制器寄存器组内部总线 等部件组成。

1

运算器

​ 运算器由 算术逻辑单元(Arithmetic and Logic Unit, ALU)、累加寄存器数据缓冲寄存器状态条件寄存 等组成。它是数据加工处理部件,用于完成计算机的各种算术和逻辑运算。

  • 算术逻辑单元 ALU 处理数据
  • 累加寄存器 AC 提供工作区,存放结果
  • 数据缓冲寄存器 DR 数据暂存,速度缓冲,单AC结构兼做操作数寄存器
  • 状态条件寄存器 PSW 保存状态标志和控制标志

控制器

​ 保证程序正确执行,且能够处理异常事件。一般包括指 令控制逻辑时序控制逻辑总线控制逻辑终端控制逻辑 几个部分。

​ 指令控制逻辑要完成取指令、分析指令和执行指令的操作,其过程分为取指令、指令译码、按指令操作码执行、形成下一条指令地址 等步骤。

  • 指令寄存器 IR 指令暂存,指令从内存中取出放入DR中,再从DR通过总线到达IR。
  • 程序计数器 PC 寄存信息、计数,指向下一条地址
  • 地址寄存器 AR 保存当前访问的指令
  • 指令译码器 ID 分析解释操作码字段,发出控制信号

多核CPU

核心,又称内核,使CPU最重要的组成部分。CPU所有的 计算、接收/存储命令、处理数据 都由 核心 执行。

1

单核多线程CPU是 交替地转换执行 多个任务。多核在理论上则是在 任何时间内 每个核执行各自的任务,不存在交替问题。因此多核速度更快。注:采用了 Intel超线程技术 的单核可以视为是双核,但 比不上 实际双核的性能。

数据表示

仅在数值为 时,各种码的相互转换 如下图 。当数值为 时各码 相同

1

数值0的原码表示有两种形式:$[+0]_原=0 0000000$ ,$[-0]_原=1 0000000$

浮点数表示时采用 IEEE754 标准,$N = 2^E \times F$ , E为解码,F为尾数。浮点数所能表示的 数值范围阶码 决定,所表示数值的 精度 则是由 尾数 决定。为了充分利用尾数来表示更多的有效数字,通常采用 规格化浮点数 。将尾数的绝对值限定在区间[0.5,1]。

IEEE754标准,一般单精度浮点个参数需牢记

数符 S(1位) 阶码 E (8位) 尾数 M (23位)
参数 单精度浮点 双精度浮点 扩充精度浮点
浮点数字长 32 64 80
尾数长度P 23 52 64
符号位S 1 1 1
指数长度E 8 11 15
最大指数 +127 +1023 +16383
最小指数 -126 -1022 -16383
指数偏移量 +127 +1023 +16383
可表示的实数范围 $10^{-38}-10^{+38}$ $10^{-308}-10^{+308}$ $10^{-4932}-10^{+4932}$

IEE754对阶码采用偏移量表示,单精度浮点数 偏移值为 127 ,双精度浮点数的偏移值为1023,临时浮点数的偏移值为16383。计算阶码时需要在 阶数的原码上加上偏移量,以单精度浮点数为例阶数为7,则$[7]_原$ = 0000 0111 ,阶码为 $[7]_原+[偏移量]_原$= 0000 0111 + 0111 1111 = 1000 0110

  • 规格化的值 :阶码部分的二进制值不全为0或1。表示的是规格化的值。
  • 非规格化的值 :当阶码部分的二进制全为0时,所表示的数是非规格化的。表示数值0,或是哪些非常接近0的数
  • 特殊值 :当阶码部分的二进制权威1时,符号位为0表示正无穷,为1表示负无穷

校验码

  • 合法编码 :用于传送数据。
  • 错误编码 :不允许在数据中出现的编码。
  • 码距 : 任意两个合法编码之间至少有多少个二进制位不同。

奇偶校验码

奇偶校验 Parity Codes ,通过在编码中加入一位校验位来使编码中的 1的个数为奇数 (奇校验),或者使1的个数为偶数 (偶校验)

数据 偶校验编码C 奇校验编码C
1010 1010 1010 1010 0 (偶数个1) 1010 1010 1 (奇数个1)
0101 0100 0101 0100 1 0101 0100 0
0111 1111 0111 1111 1 0111 1111 0
1111 1111 1111 1111 0 1111 1111 1

海明码

海明码 Hamming Code 在数据为之间的特定位置上插入k个校验码,通过扩大码距来实现检错和纠错。假设k个校验位为 $P_k,P_{k-1},…,P_1$,n个数据位为 $D_{n-1},D_{n-2},…,D_{0}$ ,对应的海明码为 $H_{k+n} … ,H_1$

  • $P_i$ 在海明码的第 $2^{i-1}$ 位置,数据位则依序从低到高占据海明码的剩余位置
  • 海明码中的任何一位都是由若干个校验位来校验的。其对应关系如下:被校验的海明位的下标等于所有参与校验该位的校验位的下标之和,而校验位由自身校验。
海明码 海明码的下标 校验位组
$H_1(P_1)$ 1 $P_1$
$H_2(P_2)$ 2 $P_2$
$H_3(D_0)$ 3=1+2 $P_1,$$P_2$
$H_4(P_3)$ 4 $P_3$
$H_5(D_1)$ 5=1+4 $P_1,P_3$
$H_6(D_2)$ 6=2+4 $P_2,P_3$
$H_7(D_3)$ 7=1+2+4 $P_1,P_2,P_3$
$H_8(P_4)$ 8 $P_8$

其中校验位的生成由校验位组的异或和决定:

$P_1 = D_0\oplus D_1 \oplus D_3$

$P_2 = D_0\oplus D_2\oplus D_3$

$P_3 = D_1\oplus D_2 \oplus D_3$

循环冗余校验码

循环冗余校验码 Cyclic Redundancy Check 广泛应用于数据通信领域和磁介质存储系统中。利用生成多项式为k个数据为产生r个校验位来进行编码。左边信息码k位,右边校验码n-k位。故又称(n,k)码。

数据结构

  • 数据: 所有能被 输入到计算机 中,且能 被计算机处理 的符号的集合,是计算机操作对象的总称
  • 数据元素: 数据中的一个个体,通常作为一个整体考虑和处理。也被称为元素、结点、记录。
  • 数据项: 具有 独立含义 的最小标识单位,也称字段或域。
  • 数据对象: 具有 相同特性 的元素的集合。
  • **数据结构: ** 指以某种内在联系将数据元素组成一个数据对象。

基本形式

$$
Data_Structure = (D,R) \
其中D为数据元素的有限集合,R为D上关系的有限集
$$

基本结构

  • 集合结构:元素关系松散

  • 线性结构:元素之间有 一对一关系

  • 树形结构:元素之间有 一对多关系

  • 图形结构:元素之间有 多对多关系

  • 逻辑结构:是对数据元素之间存在的逻辑关系的描述。

  • 物理结构:又称存储结构,是指在计算机中的表示和实现。

    • 顺序存储
    • 链式存储
    • 索引存储
    • 哈希存储

抽象数据类型

抽象数据类型(ADT)是指一个数学模型以及定义在该模型上的一组操作。通常如下表示:

1
2
3
4
5
ADT 抽象数据类型名{
数据对象:数据对象的定义
数据关系:数据关系的定义
基本操作:基本操作的定义
}

算法

算法是一个有穷的规则集合,这些规则为解决某一特定任务规定了一个 运算序列 。简单来说算法是解题的步骤。

算法描述方法

  • 自然语言:英语、汉语、日语等自然演化的语言
  • 程序设计语言
  • 流程图:传统流程图或者N-S结构图
  • 伪语言:伪代码,介于自然语言与程序设计语言之间。
  • PAD图:问题分析图,较为新颖,可以深入了解

特性

  • 有穷性:必须在有穷步之后正常结束
  • 确定性:每一步必须有确切的定义,无二义性
  • 可行性:每一条指令确实可行
  • 输入:零个或多个
  • 输出:一个或多个

算法分析

所需要的时间却决于下列因素:

  • 硬件速度
  • 书写的程序语言:实现语言越高级,执行效率越低
  • 编译程序所生成目标代码的质量
  • 问题规模

空间复杂度

指程序运行从开始到结束所需的存储量。包括两部分

  • 固定部分:主要包括程序代码、常量、简单变量、定长成分和结构变量所占空间
  • 可变部分:与算法在某次执行中处理的特定数据的大小和规模有关

时间复杂度

指程序运行从开始到结束所需要的时间。针对算法中语句执行次数做出估计。首先计算算法语句对应的语句频度,以下列c语言代码为例:

1
2
3
4
5
6
7
8
for(i=0;i<n;i++){								n
for(j=0;j<n;j++){ n^2
c[i][j]=0; n^2
for(k=0;k<n;k++){ n^3
c[i][j]=1; n^3
}
}
}

总执行次数为
$$
T(n)=2n^3+2n^2+n
\
算法的时间复杂度记为:T(n)=O(f(n))
$$
我们仅用最大次数表示算法的时间复杂度,即上文的代码时间复杂度为$T(n)=O(n^3)$

常用的世间法咋读频次计数:常数型$O(1))$ ,线性型$O(n)$ ,平方型$O(n^2)$ ,立方型$O(n^3)$ ,指数型$O(2^n)$ ,对数型$O(\log _2 n)$ ,二维型$O(n\log _2 n)$ 。

基本算法策略

穷举法

穷举法,或称枚举法、暴力破解法,是一种针对密码的破译方法,即将密码进行逐个推演指导找出真正的密码为止。

应用:求质数、百钱买百鸡

递推法与迭代法

递推是计算机数值计算中对的一个重要算法,将复杂的运算划分为可以重复操作的若干个简单运算。

典型应用:Fibonacci

分治法

将一个难以直接解决的大问题,分割 成一些规模较小的相同问题,以便各个击破。适用于规模缩小到一定程度可以解决、具有最优子结构性质、子问题的解可以合并为该问题的解、子问题相互独立的问题中。下面介绍分治法的步骤:

在每一层递归上都有三个步骤

  • 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
  • 解决:若子问题规模较小而容易被解决则直接解,否则再继续分解为更小的子问题
  • 合并:将已经求解的子问题的解逐步合并为原问题的解

贪心算法

不追求最优解,只希望得到较为满意解的方法。一般可以快速得到满意的答案。常以当前情况为基础作最优选择,而不考虑各种可能的整体情况,所以贪心算法不需要回溯

动态规划

引入一个数组,不管他们是否对最终解有用,把所有子问题的解存放于该数组中。基本思想为:把求解的问题分成许多阶段或者子问题,然后按照顺序求解各阶段或子问题,记录每个阶段决策得到的觉果序列。最后阶段的解就是初始问题的解。

适用问题:最优子结构(必须有),无后向性,子问题重叠性质

编译原理

词法分析/扫描(Scanning)

从左向右逐行扫描源程序的字符,识别出各个单词,确定 单词的类型 。将识别出的单词转换成统一的 机内表示 , 词法单元(token)形式:

1
token:<种别码, 属性值>
单词类型 种别 种别码
关键字 program, if , else… 一词一码
标识符 变量名,数组名,记录名… 多词一码
常量 整型,浮点型,字符型… 一型一码
运算符 算术、关系、逻辑 一词\一型一码
界限符 ; ( ) = { } … 一词一码

语法分析( parsing)

语法分析器 (parsing) 从此法分析器输出的token序列中 识别出各类短语 ,并构造语法分析树

1
2
position = initial + rate * 6;
<id, position> <=> <id,initial> <+> <id, rate> <*> <num,60><;>

语义分析

  • 收集标识符的属性信息

    • 种属 (kind)

    • 类型 (type)

    • 存储位置,长度

    • 作用域

    • 参数和返回值信息

  • 语义检查

    • 变量或过程未经声明就使用
    • 变量或过程名重复声明
    • 运算分量类型不匹配
    • 操作符与操作数之间的类型不匹配
      • 数组下标不是证书
      • 对非数组变量使用数组访问操作符
      • 对非过程名使用过程调用操作符
      • 过程调用的参数类型或数目不匹配
      • 函数返回类型不匹配

中间代码生成

  • 三地址码 (Three-address Code)
    • 三地址码由类似汇编语言的指令序列组成,每个指令最多有三个操作数
  • 语法结构树/语法树 (Syntax Trees)

目标代码生成

  • 目标代码生成以源程序的中间表示形式作为输入,并把它映射到目标语言
  • 目标代码生成的一个重要人物是为程序中使用的变量 合理分配寄存器

代码优化

  • 为改进代码所进行的 等价程序变换 ,使其 运行得更快占用空间更少

语言和文法

字母表(Alphabet)

  • 字母表$\Sigma$ 是一个有穷符号集合
  • 字母表的 乘积 : $\Sigma_1 \Sigma_2 = {ab|a\in\Sigma_1,b\in \Sigma_2}$
  • 字母表的 n次幂 : $\Sigma^n = \Sigma^{n-1}\Sigma$
  • 字母表的 正闭包 : $\Sigma^+ = \Sigma^1U\Sigma^2U\Sigma^3…$
  • 字母表的 克林闭包 : $\Sigma^* = \Sigma^0 U \Sigma^+$

串(String)

  • 串是字母表中 符号的 一个 有穷序列
  • 串s的 长度 通常记作 |s| ,指其中符号的个数
  • 空串 是长度为0的串,用 $\epsilon$ 表示
  • 串的连接 : x = dog , y= house , xy = doghouse
  • 串的幂运算 :若s = ba, 有 $s^1=ba, s^2=baba, s^3=bababa…$

文法

$$
G=(V_T,V_N,P,S)
$$

  • $V_T:$ 终结符集合 , 终结符(terminal symbol) 是文法所定义的语言的 基本符号 ,有时也称为token

  • $V_N:$ 非终结符集合 ,非终结符(nonterminal) 是用来表示语法成分的符号,也称语法变量

  • $P:$ 产生式集合,产生式(production) 描述了将终结符和非终结符组合成串的方法

    • 一般形式:$\alpha$->$\beta$
    • 产生式的简写:对一组有相同 左部 的产生式可以简记为:$\alpha$ -> $\beta_1 | \beta_2 | \beta_3…$ ,右部可称为 $\alpha$ 的候选式
  • $S:$ 开始符号

  • 推导(Derivations)和归约(Reductions)

    • 推导: 用产生式的左部替换产生式的左部
    • 归约: 推导的逆运算
  • 文法分类:

    • 0型文法: 无限制文法,左部至少包含一个非终结符
    • 1型文法: 上下文有关文法,左部符号的个数不准多于右部符号。不包含空产生式
    • 2型文法: 上下文无关文法,左部不能出现终结符
    • 3型文法: 正则文法(RG)
      • 右线性文法: $A$->$w B$ 或 $A$->$w$
      • 左线性文法: $A$->$B w$ 或 $A$->$w$

设计原则

为了提高软件的可维护性、可复用性,增加软件的可扩展性和灵活性,在面向对象编程的过程中我们需要遵守以下六条原则。

开闭原则

定义: 编写的代码需要对 扩展开放修改关闭 ,实现 热插拔 的效果。

例:在编写不同皮肤类的时候, 不能对抽象类进行修改 ,而是开放扩展功能,通过编写子类实现不同效果。

皮肤扩展类图

里氏代换原则

定义: 任何 基类 可以出现的地方, 子类 一定可以出现。即,子类 可以扩展 父类功能,但 不能改变 原有的功能,尽量 不重写 父类的方法。 多态频繁的程序出错的概率很大。

例:正方形若直接继承长方形,则需要修改其父类的方法。正确的方式应该是将两者抽象出一个共同的接口,用于实现。

图形继承类图

依赖倒转原则

定义: 高层模块 不应该 依赖低层模块,两者都应该 都依赖其抽象 。抽象不应该依赖于细节,细节应该依赖抽象

若A类是高层模块,B类是低层模块,那么他们应该有如下关系:

依赖关系图

例:若要组装一个电脑类,若实现的是各个部件组合于此类,则会出现不能进行具体更改的情况,应该实现将他们的接口组合于此类的效果,再实现各个接口。

电脑类组成

接口隔离原则

定义: 客户端不应该依赖于他不使用的方法。一个类对另一个类的依赖应该建立在最小的接口上。

列:若B类想要实现A类中的方法2,则不应该继承A类,而是实现方法2的接口。

接口隔离演示图

迪米特法则

定义: 又称 最少知识法则Talk only to your immediate friends and not to strangers. 如果两个软件实体 无需通信 ,那么就 不应当 发生直接的相互调用,可通过 第三方转发调用 。(降低耦合)

friend: 当前对象 本身 ,当前对象的 成员对象 ,当前对象 所建的对象 ,当前对象的 方法参数

迪米特法则实例

合成复用原则

定义: 尽量先使用组合或聚合等关联关系实现某一个实例,其次才考虑继承。通常类的复用分为 继承复用合成复用

  • 继承复用: 简单、易实例化
    • 破坏类的封装性
    • 子类与父类的耦合度高
    • 限制复用的灵活性
  • 组合/聚合复用: 将已有对象进行组合
    • 维护了类的封装性
    • 子类父类低耦合
    • 灵活性得到了保证