【国二】C语言选择题精华速记
第1节 计算机系统
一、计算机组成与工作原理
-
冯·诺依曼结构五大件运算器、控制器、存储器、输入、输出。【注意是计算机硬件组成方式】核心思想「存储程序控制」。
-
在计算机内部,指令与数据均用二进制表示
-
-
指令=操作码+地址码;执行周期=取指→分析→执行;寄存器组:PC(下条地址)、IR(当前指令)、MAR/MDR(主存地址/数据)。
-
总线:地址总线(单向,CPU→主存/I/O)、数据总线(双向)、控制总线(双向);带宽=位宽×工作频率×传输次数/8。
口诀:地单数双控双向,带宽三乘除8。
二、存储系统
-
层级:寄存器→Cache→主存→辅存;速度↓容量↑价格↓。
-
Cache-主存映射:直接、全相联、组相联;替换算法:LRU、FIFO、LFU。
-
虚拟存储:逻辑地址空间>>物理地址空间;页式、段式、段页式;缺页中断。
易错:虚拟容量由「CPU地址线位数」决定,而非硬盘大小。【详见一句话知识点】
三、CPU & I/O
-
性能指标:主频、CPI、MIPS、FLOPS;公式:CPU时间=指令数×CPI×时钟周期。
-
I/O 方式:程序查询(CPU轮询)、程序中断(异步)、DMA(块传输,周期窃取)、通道(专用处理器,并行度最高)。
-
中断流程:关中断→保存现场→执行服务→恢复→开中断→返回。
口诀:查中 DMA 通道高,中断六步记牢。
执行一条指令需要多少时间以机器周期为单位。
所谓一个机器周期就是指CPU访问存储器一次所需要的时间
机器周期的同步标准是CPU访问存储器一次所需要的时间
指令周期是取出一条指令并执行这条指令的时间。由于各条指令的操作功能不同,因此各种指令的指令周期是不尽相同的。
在计算机中,为了便于管理,常把一条指令的执行过程划分为若千个阶段,每一阶段完成一N工作。例如,取指令、存储器读、存储器写等,这每一项工作称为一个基本操作。
完成一个基本操作所需要的时间称为机器周期。
分析指令是由指令译码电路完成的,所占用的时间极短,无须分配一个完整的机器周期。般是在取指周期后期(取指结束之前的很短时间内)就可以完成
四、操作系统基础
-
四大管理:进程、存储、文件、设备;特性:并发、共享、异步、虚拟。
-
进程三态:运行-就绪-阻塞;PCB 是进程存在的唯一标志;线程=轻量级进程,共享同一地址空间。
-
调度算法:FCFS、SJF、高响应比、时间片轮转、多级反馈队列。
-
死锁 4 必要条件:互斥、占有且等待、非抢占、循环等待;处理:预防、避免(银行家算法)、检测、解除。
易错:单 CPU 任何时刻「运行态进程数≤1」。
五、数据表示与运算
-
机器数:原码(符号+绝对值)、反码(负除符取反)、补码(负反+1)、偏移码(补码符号取反)。【详见一句话知识点】
-
定点整数默认用「补码」;浮点数:阶码(移码)+尾数(原码/补码)。
-
溢出判断:双符号位(变形补码)或进位异或。
口诀:正数三码同,负反补看符号;溢出看双符。
第2节 数据结构与算法
一、线性结构
-
顺序表:逻辑相邻→物理相邻;随机存取 O(1),插入/删除 O(n)。
-
链表:单、双、循环;存储空间可以不连续;插入/删除 O(1)(已知指针)。
-
栈:LIFO;顺序栈 top=-1 空,top=MAX-1 满;链栈无满溢,top=bottom=NULL 为空。
-
队列:FIFO;循环队列判空/判满:牺牲一个单元或加 size 标志。
口诀:栈顶进出队列尾进头出,循环留一格。
详见后文栈、队列、线性表、存储结构
二、树与二叉树【注意:度为2小于度为0】
-
基本术语:度、叶子、深度、满二叉树、完全二叉树。
-
性质:
-
第 i 层最多 2^(i-1) 结点;深度 k 最多 2^k-1 结点。
-
n0=n2+1(叶子=度为2结点+1)。
-
-
遍历:
-
前序:根-左-右;中序:左-根-右;后序:左-右-根;层序:逐层从左到右。
-
已知前+中 or 后+中可唯一确定二叉树;前+后不能。
-
-
转换:树←→二叉树(左孩子右兄弟);森林←→二叉树。
-
哈夫曼树:带权路径长度最短,n 个叶子需 n-1 次合并。
易错:完全二叉树深度 ⌊log₂n⌋+1;第 1 层深度=1。
二叉树的性质
性质1:二叉树第i层上的节点数目最多为 2{i-1} (i≥1)。
性质2:深度为k的二叉树至多有2{k}-1个节点(k>=1)。
性质3:包含n个节点的二叉树的高度至少为log2 (n+1)。
性质4:对于任何一棵二叉树,如果其终端结点数(叶子节点数)为n0,度为2的结点数为n2,则n0 = n2 + 1
三、图
-
存储:邻接矩阵 O(n²)、邻接表 O(n+e)。
-
遍历:DFS(栈/递归)、BFS(队列)。
-
最小生成树:Prim(点优先)、Kruskal(边优先)。
-
拓扑排序:AOV 网,入队 0 入度顶点;可检测环。
口诀:Prim 点密 Kruskal 边疏,拓扑零入读。
四、查找
-
顺序查找:O(n);折半查找:有序顺序表 O(log n),最多比较 ⌊log₂n⌋+1 次。
-
分块查找:块内无序、块间有序,索引+顺序。
-
哈希:构造(直接、除留余数、平方取中)、冲突处理(开放定址、链地址)。
易错:折半必用「顺序存储+有序表」。
五、排序(时间/空间/稳定性)
-
插排:O(n²) 稳定;希尔:O(n^1.3) 不稳定。
-
冒泡:O(n²) 稳定;快速:O(n log n) 平均,O(n²) 最坏,不稳定。
-
选择:O(n²) 不稳定;堆排:O(n log n) 不稳定,辅助 O(1)。
-
归并:O(n log n) 稳定,需 O(n) 辅助;基数:O(d×n) 稳定。
口诀:快堆选不稳,插冒归基稳,快希堆选空 O(1)。
六、算法基础
-
算法 5 特性:有穷、确定、可行、输入、输出。
-
复杂度:
-
时间 T(n):最坏、最好、平均;大 O 表示上界。
-
空间 S(n):原地工作=辅助空间 O(1)。
-
-
常见阶:O(1)<O(log n)<O(n)<O(n log n)<O(n²)<O(2ⁿ)。
易错:算法优劣与运行环境无关,与规模有关。
第3节 程序设计与软件工程
一、软件生命周期(SDLC)
-
阶段划分
-
可行性研究与计划 → 回答“能不能做”
-
需求分析 → 回答“做什么”【产出:需求规格说明书 SRS】
-
系统设计 → 回答“怎么做”【概要设计+详细设计】
-
编码实现
-
测试 → 单元→集成→确认(验收)→系统
-
运行维护 口诀:可否需设编测运维。
-
-
关键文档
-
需求阶段:SRS(Software Requirement Specification)
-
设计阶段:概要设计说明书、详细设计说明书
-
测试阶段:测试计划、测试用例、测试报告
-
交付阶段:用户手册、操作手册。
-
二、软件开发模型(选模型看场景)
-
瀑布模型:阶段顺序、一次性交付,需求稳定。
-
增量模型:分批次增量交付,需求渐增。
-
螺旋模型:风险驱动,每圈都计划/风险分析/评估,大型复杂。
-
V模型:测试级别与开发阶段一一对应,强调早期验证。
-
敏捷/迭代:小步快跑、拥抱变化,强调“可工作的软件”>文档。 高频题:需求易变→敏捷;需求明确且稳定→瀑布。
三、结构化方法(传统方法学)
-
指导思想:自顶向下、逐步求精、模块化。
-
三大利器:
-
数据流图(DFD)→描述“数据”在系统中的流动与加工。
-
数据字典(DD)→对DFD中所有数据项、文件、加工进行定义。
-
结构化语言/判定表/判定树→描述加工逻辑。
-
-
设计阶段:
-
概要设计:划分模块、确定调用关系 → 结构图(SC图)。
-
详细设计:每个模块的算法 → 程序流程图、N-S图、PAD图、PDL。 注意:DFD 用于需求分析,不能出现在详细设计。
-
四、面向对象方法
-
三大机制:
-
封装:数据+操作→对象,信息隐蔽。
-
继承:复用与扩展,子类→父类。
-
多态:同一消息不同响应(重载/重写)。
-
-
基本概念
-
类:对象的抽象模板。
-
对象:类实例,具有唯一标识、状态、行为。
-
消息:对象间通信机制。
-
关系:关联、聚合、组合、泛化(继承)、依赖。
-
-
UML 图(统一建模语言)
-
用例图:需求捕获,参与者+用例。
-
类图:静态结构。
-
顺序图/协作图:对象间动态交互。
-
状态图:对象生命周期。
-
活动图:业务流程/用例流程。 口诀:用类序状活。
-
五、软件测试
-
测试目的:发现错误(而非证明正确)。
-
测试级别
-
单元测试:最小模块,白盒为主,依据详细设计。
-
集成测试:模块接口,依据概要设计;策略:一次性/增量(自顶向下、自底向上、混合)。
-
确认测试(验收测试):黑盒,对照 SRS 检查功能与性能。
-
系统测试:把软件放进整个计算机系统,与硬件、外设、网络、数据、人员一起测。
-
-
测试方法
-
黑盒:功能视角,不看代码。用例设计技术:等价类、边界值、错误推测、因果图、判定表驱动。
-
白盒:结构视角,看代码。覆盖标准:语句覆盖→判定覆盖→条件覆盖→判定-条件覆盖→条件组合覆盖→路径覆盖(最强)。(边界值覆盖不属于白盒测试方法)
-
灰盒:介于二者,关注接口与交互。 口诀:黑功能白结构灰接口。
-
-
测试用例组成:输入数据 + 预期输出结果。
-
调试(排错)目的:定位并改正错误,与测试不同。
六、软件质量与评价
-
质量模型(ISO/IEC 9126/25010)
-
功能性、可靠性、易用性、效率、可维护性、可移植性。
-
-
McCabe 环路复杂度:V(G)=m-n+2,值越大出错概率越高。
-
可靠性指标:平均无故障时间 MTBF、平均修复时间 MTTR。
七、软件项目管理(高项浓缩)
-
进度管理:甘特图、PERT 图(关键路径)。
-
成本估算:COCOMO 系列、功能点 FP。
-
风险管理:风险识别→定性/定量分析→应对(规避、转移、减轻、接受)。
-
配置管理:版本控制、变更控制、基线。
-
CMMI 五级:初始→已管理→已定义→量化管理→优化。
八、软件设计工具
系统结构图
九、常考易错速记
-
结构图宽度 = 同一层最多模块数;深度 = 层数;扇出 = 直接调用下级模块数;扇入 = 被上级直接调用数。
-
数据流图(DFD)箭头 = 数据流;圆/椭圆 = 加工;双横线 = 数据存储;矩形 = 外部实体。
-
对象三特征:封装(对象是属性和方法的封装)、继承、多态;无“复用”——复用是结果而非特征。
-
操作是对象的动态属性
-
软件≠程序:软件 = 程序 + 数据 + 文档。
-
测试与调试区别:测试找错,调试改错;测试可计划,调试靠经验。
-
需求分析阶段绝不会产生“算法详细设计”文档——那是详细设计阶段的事。
-
白盒方法里最弱覆盖:语句覆盖;最强:路径覆盖。
-
边界值覆盖不属于白盒测试方法
-
敏捷宣言四句话:个体互动>流程工具;可工作软件>详尽文档;客户合作>合同谈判;响应变化>遵循计划。
-
结构化程序强调程序的易读性
-
软件工程三要素:方法、工具、过程
第4节 数据库设计基础
一、数据管理发展
-
三阶段:人工管理→文件系统→数据库系统;数据库阶段「数据冗余最小、共享性最高、独立性最高」。
口诀:人文件库,冗余渐小独立渐高。
二、数据模型
-
三要素:数据结构、数据操作、数据约束。
-
模型分类:
-
概念模型:E-R 图,与 DBMS 无关;实体-属性-联系(1:1、1:n、m:n)。【详见E-R图(Entity–Relationship 中文:实体-联系)】
-
逻辑模型:层次、网状、关系、面向对象;DBMS 实现。
-
物理模型:存取方法、索引、存储路径。
-
-
E-R 图符号:矩形实体、椭圆属性、菱形联系、连线标基数。
易错:m:n 联系必须单独转关系,不能放两端实体。
三、数据库结构
在数据库技术中,为提高数据库的逻辑独立性和物理独立性,数据库的结构被划分成
用户级、存储级、 概念级
数据库三级模式结构
级别 | 别名 | 描述 | 作用 |
---|---|---|---|
外部级 | 用户级、子模式 | 用户看到的数据视图 | 实现逻辑独立性(应用不依赖全局逻辑结构) |
概念级 | 逻辑级、模式 | 数据库整体逻辑结构(表、字段、关系) | 独立于存储细节和用户视图 |
内部级 | 存储级、物理模式 | 数据在磁盘上的存储方式(文件、索引、压缩) | 实现物理独立性(逻辑结构不依赖存储方式) |
四、关系模型
-
术语:关系(表)、元组(行)、属性(列)、域(取值范围)、候选码、主码、外码。
关系模型中,键的定义是能够唯一限定一条元组的属性或属性的集合,则成为键。术语 含义 实例 关系 采用二维表表示,由表框架及元组组成 学生登记表就是一个关系 属性 二维表中的一列 学生登记表的属性有学号、姓名、系号等 属性元数 二维表中属性的个数 学生登记表属性元数为 5 值域 每个属性的取值范围 学生登记表“年龄”属性的值域不能为负数 元组 二维表中的一行 (06001,方,01,22,男)就是一个元组 候选码 能唯一标识元组的最小属性集 姓名不重名时,学号、姓名都是候选码 主键(主码) 从候选码中选定的唯一标识 选定学号作为唯一标识,学号就是主码 外键(外码) 表M中的属性集是表N的候选码或主码 学生登记表中的“系号”是系信息表的主码,故为学生登记表 -
完整性:
-
实体:主属性不能空。
-
参照:外码要么空要么等于被参照表主码值。
-
用户定义:具体业务规则。
-
-
关系代数:
-
关系代数详解
-
在关系代数中,常用的符号包括:
- 选择:σ,用于从关系中选择满足特定条件的元组。
- 投影:π,用于从关系中选择特定的列。
- 连接:⨝,用于将两个关系的元组基于指定条件组合在一起。
- 并:∪,用于合并两个关系的元组。
- 差:−,用于从一个关系中去除另一个关系的元组。
- 不等于:符号<>,
这个表达式代表A不等于B且D>=2的结果
这个表达式代表A=B且C大于4D大于3的结果
- 笛卡尔积:×,用于生成两个关系的所有可能元组组合。
-
四、范式与分解
-
1NF:属性原子不可分。
-
2NF:在 1NF 基础上消除「非主属性对码的部分依赖」。
-
3NF:在 2NF 基础上消除「非主属性对码的传递依赖」。
-
BCNF:决定因素必为候选码。
快速判断:
-
部分依赖:码为组合属性,非主属性依赖其中一部分 → 不满足 2NF。
-
传递依赖:A→B→C,其中 A 码,B 非码,C 非主 → 不满足 3NF。
口诀:部传多值,一二三BC。
五、数据库设计步骤
-
需求分析 → 数据流图、数据字典。
-
概念设计 → E-R 图。
-
逻辑设计 → E-R 转关系模式,规范化。
-
物理设计 → 索引、簇集、分区、存储参数。
-
实施运行维护。
口诀:需概逻物施维。
六、SQL 四大类
-
DDL:CREATE/DROP/ALTER——定义结构。
-
DML:INSERT/UPDATE/DELETE——操纵数据。
-
DQL:SELECT——查询。
-
DCL:GRANT/REVOKE/COMMIT/ROLLBACK——权限事务。
口诀:定操查权。
七、事务与并发
-
ACID:原子、一致、隔离、持久。
-
并发问题:丢失更新、脏读、不可重复读、幻读。
-
封锁:排他锁(X)、共享锁(S);协议:一级(防丢失)、二级(防脏读)、三级(防不可重复读)。
口诀:锁 SX 一二三,隔离级别逐层加。
八、常考场景速记
-
1:1 联系:可合并也可各放外码;1:n 把外码放 n 方;m:n 必须新建关系。
-
“选课”模式:SC(S#,C#,G) 主码(S#,C#),外码各自参照 S 和 C。
-
自然连接结果属性列:两表列并集去掉重复名,行按公共属性值相等匹配。
-
除运算:R÷S 得到“在 R 中能找到 S 全部组合”的那些元组。
✅✅✅一句话知识点
C语言源程序名的后缀是c,
对源程序进行编译后得到一个后缀为.obj的目标释序,
然后把目标程序讲行连接操作,则可得到一个后缀为exe的可执行文件
C语言程序是由函数构成的
C语言不能散套定义面数,但可以嵌套调用函数
C语言中的 main() 函数可以单独进行编译
除了main函数每个函数都需要被其他函数调用才能执行,函数不可以单独执行,但是可以作为单独文件形式存在
[X]虚拟存储器的空间大小就是头际外存的大小[出现绝对词”就是“选项错误]
理论上计算机虚拟内存最大容量取决于计算机地址位数
采用虚拟存储管理技术的主要优点是可为用户提供比物理内存大的多的逻辑地址空间
虚拟存储器的空间大小取决于计算机的访存能力
虚拟存储器是内存储器
虚拟存储器是对主存的扩展
操作系统在进程管理的以下方面做工作:进程控制、进程同步、进程通信和进程调度
进程管理的主要工作是处理器调度
进程调度仅负责对 CPU 进行分配【记忆:jin】
进程具有多种属性,并发性之外的另一重要属性是动态性
为了描述进程的动态变化过程,在进程控制块中定义了进程状态字
分时操作系统的特点有:
(1)交互性(同时性):用户与系统进行人机对话。用户在终端上可以直接输入、调试和运行自己的程序,在本机上是修改程序中的错误,直接获得结果。
(2)多路性(多用户同时性):多用户同时在各自终端上使用同-CPU和其他资源,充分发挥系统的效率。
(3)独立性:用户可彼此独立操作,互不干扰,互不混淆。
(4)及时性:用户在短时间内可得到系统的及时回答
下列存储管理中要采用静态重定位技术的是可变【adj,形容词】分区存储管理
【一般选项搭配请求...,选可变】
地址重定位方式包括静态地址重定位和动态地址重定位
【静态地址重定位必须占用连续固定的内存空间】【军训站队】
【静态地址重定位是在程序执行之前进行的】【先站队】
【动态地址重定位是在程序执行期间进行的】【后跑操】
【动态地址重定位不要求程序装入固定的内存空间,且允许程序在内存中再次移动位置】【灵活】
原码
反码:除符号位每位求反
补码:反码末尾加1
偏移码:补码符号位取反
整数在计算机中存储和运算通常采用的格式是补码
正整数的原码、反码和补码是一样的。
等待 就绪 运行 阻塞 的关系
进程一旦创建完成,就进入就绪状态
被阻塞的进程在其被阻塞的原因获得解除后即进入就绪状态
当处于运行状态的进程申请新资源而又不能立即被满足时即进入阻塞状态
当一个进程在运行过程中释放了系统资源后要调用唤醒进程原语
当一进程在运行状态下结束时要调用撤销进程原语
进程3种状态转换条件如下:
(1)处于就绪状态的进程、一旦分配到CU、就转为运行状态。
(2)处于运行状态的进程,当需要等待某个事件发生才能继续运行时,则转为等待状态(阻塞状态);或者由于分配给它的时间片用完,就让出CPU而转为就绪状态。
(3)处于等待状态的进程,如果它等待的事件已经发生,即条件得到满足,就转为就绪状态
软件测试的实施步骤是单元测试,集成测试,确认测试
通常所说的计算机主机包括中央处理器和主存储器
分时操作系统通常采用时间片轮转策略为用户服务。
采用时间片轮转算法调度的目的是使得多个进程都能得到系统的及时响应
CPU 中指令寄存器的任务是 保存当前正在执行的指令
CPU 可以通过总线访问计算机内存和各种输入输出设备【CPU能直接访问内部寄存器(register),高速缓冲存储器(cache),内存储器(RAM与ROM)】
在执行指令过程中,CPU不经过总线能直接访问的是寄存器
下列存储器中访问速度最快的是缓存【因为缓存最快,故缓冲提高速度】
计算机中的缓冲技术用于提高主机和设备交换信息的速度
用来解决 CPU 和主存之间速度不匹配问题的方法是在主存储器和 CPU 之间增加高速缓冲存储器
指令中的地址部分给出了存放操作数所在地址的寻址方式是间接寻址
I/O方式中使计算机系统并行作程度最高的是通道[I/O方式题 一般选 选项最长的]
口诀:上海机场【SHJC】
数据库系统(DBS)包括数据库(DB〉和数据库管理系统(DBMS)
强制类型转换的正确用法与易混表达
-
C 语言规定:帽子必须 整个扣在表达式外面,写法是
(类型)表达式
例如
(int)(a + b)
-
C++ 额外允许“函数式”写法
类型(表达式)
例如
int(a + b)
在 纯 C 编译器里遇到 int(a + b)
,会把 int
当成“函数名”去解析,而 int
又是关键字,关键字不能当函数名,于是报 syntax error——这就是“与关键字重名”的实际含义。
✅【国二挖坑清单】
易错排行榜
- 表达式计算顺序理解错误:
NUM=(2+1+1)*2+1/2=8
A没有列举完全x的取值情况,所以不等价
循环队列“还剩多少个元素”
//剩余元素数 = (rear − front + m) % m
//题目里 front = 30,rear = 10,m 为总容量,代入:
//(10 − 30 + m) % m = (m − 20) % m = m − 20
场景 | 公式 |
---|---|
还剩多少元素 | (rear − front + m) % m |
空队 | (rear + 1) % m == front |
c源代码不允许写二进制,只给了十、八、十六 这几种写法
-=
是复合赋值运算符,优先级低于算术运算符 +
求表达式 x -= x + x
x = 10
右边:x + x → 10 + 10 = 20
整个式子:x -= 20 → x = x - 20 = 10 - 20 = -10
在 switch 语句中,不一定使用 break 语句
%(求余)运算符的运算对象只能是整型
a = a++, i++; | ✅ 可以 | 逗号表达式,左边 double,右边 int,整体值丢弃,合法 |
求系统结构图深度
题干 1(深度 = 4)
链条举例:
某系统 → 功能2 → 功能2.2 → 功能2.2.1
一共 4 个结点,所以深度 = 4
题干 2(深度 = 3)
链条举例:
某系统 → 功能2 → 功能2.1
最远只有 3 个结点,所以深度 = 3
坑 1 gets 出现即错
真题 2023-03 卷 程序填空第 1 空
char s[80];
________(s); /* 标准答案:fgets */
题干提示:
“原程序使用 gets 函数,已被 C11 标准废除,请替换为安全函数。”
→ 直接送 2 分,填 fgets 即可。
坑 2 忘记 fclose
真题 2022-09 卷 改错题第 8 行
FILE *fp;
fp = fopen("out.dat","w");
fprintf(fp,"%d\n",sum);
/* 后面直接 return 0; */
标准答案:在 return 0; 前补 fclose(fp);
评分细则:漏写 fclose 扣 1 分;写成 fclose("out.dat")
再扣 1 分。
坑 3 while(!feof(fp)) 直接读
真题 2021-12 卷 选择题 17
while (!feof(fp)) {fscanf(fp, "%d", &x);sum += x;
}
问:循环次数比文件实际数据个数( )
A. 相等 B. 多 1 次 C. 少 1 次 D. 不确定
答案:B(先判后读,结束标志多读一次)
坑 4 fgets 把 '\n' 读走 → puts 再补 '\n' 出现空行
真题 2020-09 卷 程序填空第 3 空
fgets(buf, 80, fp);
puts(buf); /* 结果多一个空行 */
标准答案:buf[strlen(buf)-1] = '\0'; /* 去 \n */
评分:填 *(buf+strlen(buf)-1)=0;
也给分。
坑 5 scanf 返回“成功读入项数”
真题 2023-09 卷 选择题 9
int a, b, ret;
ret = scanf("%d%d", &a, &b);
printf("%d", ret);
输入:12 回车
问:输出结果( )
A. 0 B. 1 C. 2 D. 3
答案:C(成功读入 2 项,ret=2)
额外彩蛋:综合大题模板(国二压轴 18 分)
题干结构年年一样
-
打开文件
-
循环 fscanf / fgets 读
-
处理数据
-
写文件 fprintf / fputs
-
fclose
改错/填空 90% 分布在:
-
① fopen 模式写错 → 扣 2 分
-
② while(!feof()) 位置 → 扣 2 分
-
③ 少 fclose → 扣 1 分
-
④ gets 未改 → 直接 0 分(安全分全丢)
✅判断关系”一对多“还是”多对多“
左→右 | 右→左 | 联系类型 |
---|---|---|
1 | 1 | 一对一 |
1 | 多 | 一对多 |
多 | 多 | 多对多 |
✅易混排序法
排序算法“交换行为”对比
排序法 | 交换方式 | 是否每次交换产生新逆序 | 特点 |
---|---|---|---|
冒泡排序 | 相邻交换 | ✅ 是 | 每次交换反转相邻元素顺序 |
快速排序 | 远距离交换 | ❌ 否 | 交换不相邻元素,不保证产生新逆序 |
简单插入排序 | 元素平移 | ❌ 否 | 元素后移/前移,不会引入新逆序 |
简单选择排序 | 选择后交换 | ❌ 否 | 只交换一次,不产生新逆序 |
冒泡排序每经过一次元素的交换会产生新的逆序
排序法 | 通俗比喻 | 核心动作 | 是否稳定 | 平均速度 | 选择题坑点 |
---|---|---|---|---|---|
冒泡排序 | 水泡上浮 | 相邻两两比,小的往前冒 | ✅稳定 | 慢 O(n²) | 每次交换产生新逆序 |
简单选择 | 选最小值 | 扫一圈选最小,直接放最前 | ❌不稳定 | 慢 O(n²) | 交换次数最少 |
简单插入 | 摸牌插牌 | 一张张插到已排好的队里 | ✅稳定 | 快 O(n²) | 平移不产生新逆序 |
快速排序 | 分治挖坑 | 找个基准,小的扔左边大的扔右边 | ❌不稳定 | 最快 O(n log n) | 远距离交换 |
四句口诀背下来:
-
见“每次交换产逆序”→ 冒泡
-
见“交换最少”→ 选择
-
见“平移插入”→ 插入
-
见“最快平均”→ 快速
“冒逆序,选最少,插平移,快最快。”
各种排序最坏情况比较次数(n 很大时)【堆排序比较次数最少】
排序法 | 最坏比较次数(n 很大) | 量级 | 10 秒口诀 | 考题关键词 |
---|---|---|---|---|
冒泡排序 | 0.5 n² | O(n²) | 冒泡冒到底,比到吐 | 每次交换产逆序 |
简单选择 | 0.5 n² | O(n²) | 选妃全场看一遍 | 交换次数最少 |
直接插入 | 0.5 n² | O(n²) | 插牌每张都挪一遍 | 平移不产生逆序 |
快速排序 | 0.5 n²(已退化) | O(n²) | 快速退化也 n² | 远距离交换 |
希尔排序 | 无封闭公式 | >n log n 且 <n² | 步长定好坏 | 无固定值 |
堆排序 | 2n log₂n | O(n log n) | 堆是二叉树,只比 log 层 | 最坏比较最少 |
✅易混指针表达式
优先级是分水岭【✅指针运算++的不同情况】
优先级高 → 先执行;后缀 ++
高于前缀 *
;括号可强行改变。
表达式 | 编译器拆解 | 第一步 | 第二步 | 结果类型 |
---|---|---|---|---|
*p + 1 | (*p) + 1 | 取 p 的目标值 | 值 +1(纯算术) | int |
*p++ | *(p++) | 指针 p 先移动 | 取移动前地址里的值 | int |
(*p)++ | — | 取 p 的目标值 | 把该值 +1 写回原地址 | int |
++p | — | 指针 p 自身 +1 | 返回新地址 | int * |
内存图一句话
-
*p + 1
只算账,不改内存,也不动指针 -
*p++
先取老值,再让指针滑向下一个元素 -
(*p)++
把当前元素原地 +1,指针不动 -
++p
指针自己先走,不关心值
例:
int aa[3][3] = {{2}, {4}, {6}};
main
{int i, *p = &aa[0][0]; for (i = 0; i < 2; i++){if (i == 0)aa[i][i + 1] = *p + 1; else++p; printf("%d", *p);}}//求输出
第一次循环 i == 0
-
aa[i][i+1]
→aa[0][1]
→ 正是 下标 1 -
*p + 1
→ 2 + 1 → 3 -
执行语句aa[i][i+1]=*p+1后 赋值结果:下标 1 处的值变成 3
但 p 本身丝毫未动,它仍指向下标 0!
步骤 | 下标 0 | 下标 1 | 下标 2 | ... |
---|---|---|---|---|
赋前值 | 2 | 0 | 0 | ... |
赋后值 | 2 | 3 | 0 | ... |
p 指向 | ✅ |
立刻 printf("%d", *p);
→ 输出 2(因为 p 还在下标 0)
第二次循环 i == 1
-
else ++p;
→ p 先自增,现在指向下标 1(值已变 3) -
printf("%d", *p);
→ 输出 3
总输出23
✅区分指针与数组的用法
一、4 个选项逐条剖尸
选项 | 代码 | 结果 | 错在哪 |
---|---|---|---|
A | char *s; s = "Olympic"; | ✅合法 | 指针指向字符串常量首地址,标准写法。 |
B | char s[7]; s = "Olympic"; | ❌非法 | 数组名 s 是地址常量,不能当左值被重新赋值。 |
C | char *s; s = "Olympic"}; | ❌非法 | 右边多一个 } ,属于词法错误(编译直接报错)。 |
D | char s[7]; s = {"Olympic"]; | ❌非法 | 花括号初始化只能在定义时一起写,不能分开;且引号不配。 |
数组名所表示的数组地址不能被重新赋值
二、知识点速记
1. 两种“字符串载体”区别
载体 | 本质 | 能否整体赋值 | 典型写法 |
---|---|---|---|
char *p | 指针变量 | ✅ 可再指向别处 | p = "abc"; |
char a[N] | 地址常量 | ❌ 不可整体改 | strcpy(a, "abc"); // 用拷贝 |
strcat(p1,p2)函数将*p1和*p2指向的两个串连接起来
[常与strcpy联合使用]
注意strcpy的用法:
strcpy 只做“复制”这一件事,绝不帮你分配内存。
因此调用前必须保证:
(1)目标指针指向一块足够大的、可写的内存
(2)源字符串必须以 '\0' 结束(strcpy 会把它一起拷过去)
例:
语句回顾
char str1[] = "string"; // 7 字节已分配,内容可改
char str2[8]; // 8 字节已分配,内容可改
char *str3; // 只定义了指针,**没指向任何有效内存**
char *str4 = "string"; // 指针指向**字符串常量**(只读段)
C 项:str3 未指向有效内存
strcpy(str3, "HELL03"); // ❌
-
str3
仍是野指针,值是随机地址 -
strcpy 会把 7 字节内容(含 '\0')抄到那个随机地址 → 段错误 / 崩溃
-
正确做法必须先让 str3 “有地方住”:
str3 = malloc(20); // 或 char buf[20]; str3 = buf;
if (str3) strcpy(str3, "HELL03");
D 项:str4 指向只读常量
strcpy(str4, "HELL04"); // ❌ 编译能过,运行崩
-
str4 = "string"
让指针指向字符串常量(通常位于 .rodata 段,只读) -
strcpy 试图往只读内存写数据 → 运行时 segmentation fault
-
想改内容,必须让 str4 重新指向可写内存:
char buf[20] = "string";
str4 = buf; // 现在 str4 指向可写区
strcpy(str4, "HELL04"); // ✅ 合法
2. 初始化 vs 赋值
-
定义时一起给值:
char a[8] = "abc";
✅ -
定义后再整体给:
a = "abc";
❌ 常量地址不能当左值!
3. 字符串常量规则
-
双引号配一对,大括号只在数组初始化且定义同时出现才合法。
4.指针与二维数组的实际关系
因为 p
被初始化为 &x[1][1]`,也就是二维数组里“第 1 行第 1 列”那个元素的地址。
在 C 里,二维数组在内存是一行接一行的连续存储,所以:
下标线性展开(按字节顺序):
x[0][0] x[0][1] x[0][2] | x[1][0] x[1][1] x[1][2] | x[2][0] x[2][1] x[2][2]0 1 2 3 4 5 6 7 8
5.区分指针数组,数组指针,函数指针
“括号谁在前,就先是谁。”
-
*ptr[M]
[] 在前 → 先数组,后指针 → 指针数组 -
(*ptr)[M]
* 在前 → 先指针,后数组 → 数组指针 -
(*ptr)(...)
* 在前 → 先指针,后函数 → 函数指针
知识点卡片(背完不丢分)
名称 | 声明模板 | 关键字 |
---|---|---|
指针数组 | T *p[N]; int *ptr[M]; | 先数组,后指针 |
数组指针 | T (*p)[N]; int (*ptr)(int); | 先指针,后数组 |
函数指针 | T (*p)(...); int (*ptr)[M]; | 先指针,后函数 |
✅【补充,使用花括号{}的情况】
只有“聚合类型”(数组 / 结构体 / 联合体)在定义时想一次性给多个成员赋初值**,才用花括号;普通变量、后面再赋值都不用。**
1️⃣ 必须用花括号的 3 个场景
场景 | 示例 | 说明 |
---|---|---|
数组定义时整体赋初值 | int a[4] = {1,2,3,4}; | 一次写多元素 |
结构体定义时整体赋初值 | struct Point p = {3,4}; | 一次写多字段 |
联合体定义时整体赋初值 | union U u = {.x=5}; | 一次写首成员 |
2️⃣ 绝对不要用花括号的情况
错误写法 | 原因 |
---|---|
char s[7]; s = {"Olympic"}; | 定义已结束,再赋值只能逐个或 strcpy |
int a[3]; a = {1,2,3}; | 同上,数组名是常量地址,不能整体改 |
int x = {5}; | 单变量用花括号语法不错但风格冗余,直接 int x = 5; |
✅指针运算++的不同情况
关键看 运算符的“优先级”和“结合方向”,再记住一句口诀:
“后加号跟谁亲,就先干谁的活。”
1️⃣ 两种 ++
的底层区别
形式 | 正式名称 | 作用对象 | 结果类型 | 典型案例 |
---|---|---|---|---|
*t++ | “先取物,后指针+1” | 指针 t | 仍是 char | 字符串遍历 |
(*t)++ | “先取物,后数值+1” | 指针指向的那块内存 | 仍是 char | 计数器/数组元素累加 |
2️⃣ 优先级说了算
C 运算符优先级(从高→低)
-
括号
()
-
后缀
++
(即t++
) -
前缀
*t
(取内容)
因此:
-
*t++
≡*(t++)
// 后缀++
优先级高,指针先移动 -
(*t)++
// 括号强制先取内容,数值再加 1
3️⃣ 内存视图跑一遍
char a[3] = "Hi";
char *t = a; // t → 地址 1000
表达式 | 步骤拆解 | 执行后 t 值 | 内存变化 |
---|---|---|---|
ch = *t++; | ① ch = *t → 'H'② t = t+1 | 1001 | 无 |
ch = (*t)++; | ① ch = *t → 'H'② *t = *t+1 | 1000 | a[0] 由 'H'→'I' |
✅常考的“字符/字符串/文件”函数
一、字符读写(选择题必现)
函数 | 频度 | 国二速记 |
---|---|---|
getchar() | ★★★ | 键盘→int,行缓冲,回显
从标准输入(通常是键盘)读取一个字符并返回 |
putchar(int c) | ★★★ | int→屏幕,行缓冲 |
getch() | ★★ | 键盘→int,无缓冲,不回显( conio.h ) |
函数 | 读入单位 | 能否读空格 | 能否读换行 | 返回值 | 常考陷阱 |
---|---|---|---|---|---|
getc | 1 个字符 | ✅ 能 | ✅ 能 | int (需强转 char) | 需循环才能拼成串 |
getchar | 1 个字符 | ✅ 能 | ✅ 能 | int (需强转 char) | 同 getc,只是宏 |
gets | 整行(含空格)[字符串] | ✅ 能 | ❌ 自动丢弃换行 | char * | 已废弃——无边界检查,国二禁用 |
二、字符串读写(填空/改错高频)
函数 | 频度 | 国二速记 |
---|---|---|
gets(str) | ★★ | 已废弃!考卷出现即错(溢出),用 fgets 替代 |
fgets(str, n, stdin) | ★★★ | 读 n-1 字符或遇 '\n' 结束,留 '\0',安全 |
puts(str) | ★★ | 自动加 '\n' |
fputs(str, stdout) | ★ | 不自动加 '\n'` |
三、格式化读写(必背原型)
函数 | 频度 | 国二速记 |
---|---|---|
scanf("格式", &变量) | ★★★ | 返回成功读入项数, EOF 表示失败 |
printf("格式", 变量) | ★★★ | 返回输出字符数 |
fprintf(fp, "格式", ...) | ★★ | 写文件 |
fscanf(fp, "格式", ...) | ★★ | 读文件 |
四、文件打开/关闭(大题模板)
函数 | 频度 | 国二速记 |
---|---|---|
fopen("文件名", "模式") | ★★★ | 模式只记 3 组: "r"读、"w"写(清空)、"a"追加;加 b 二进制 |
fclose(fp) | ★★★ | 返回 0 成功,EOF 失败;不关闭丢数据 |
feof(fp) | ★★ | 读到文件尾才真,先读后判 |
五、块读写(程序填空最爱)
函数 | 频度 | 国二速记 |
---|---|---|
fread(buf, size, n, fp) | ★★ | 返回实际读入“块数” |
fwrite(buf, size, n, fp) | ★★ | 返回实际写出“块数” |
六、文件操作 4 步口诀(背下来就够用)
“打开→读写→关,模式 r 读 w 写 r+ 读写,失败 NULL 要判断。”
1. 打开
FILE *fp;
fp = fopen("文件名", "模式"); // 成功返回指针,失败返回 NULL
模式 | 含义(只记 3 个) |
---|---|
"r" | 读,文件必须存在 |
"w" | 写,文件不存在就新建,存在就清空 |
"r+" | 读写,文件必须存在,可改可写 |
2. 读写(国二只考 3 个)
函数 | 作用 | 记忆 |
---|---|---|
fscanf(fp, "%d", &x) | 像 scanf,但从文件读 | “前面加 f 就是文件版” |
fprintf(fp, "%d", x) | 像 printf,但写到文件 | 同上 |
fgets(str, n, fp) | 读一行(含 \n ) | “fgets 会带走回车符” |
3. 关
fclose(fp); // 写完一定要关,否则数据丢
七、fgets 用法 10 秒图解
char buf[80];
fgets(buf, 80, fp); // 最多读 79 字符 + '\0',遇到 '\n' 也停
结果:
-
文件一行是
abc\n
→ buf 里存'a','b','c','\n','\0'
-
所以
puts(buf);
会先打印abc
,再自动加 '\n' → 屏幕多空一行!
去回车模板(国二填空年年考):
buf[strlen(buf)-1] = '\0'; // 把最后一个 '\n' 改成结束符
八、国二最爱挖的“坑位”清单
-
gets 出现即错 → 改
fgets
-
scanf 返回“成功项数” → 选择题常问“运行后变量值/函数返回值”
-
打开文件后忘记 fclose → 模板最后补
fclose(fp);
-
feof 先读再判 → 不能 while(!feof(fp)) 直接读
-
fgets 读走 '\n' → 与
puts
自动加 '\n' 配合,常多一空行
“字符 get/put 缓冲分,字符串 gets 已废用;
格式 scanf 返回数,文件 r w a 加 b 弄清;
块读写 fread fwrite 返回块,
打开 fclose 必须成,feof 别忘先读后判真。”
✅栈、队列、线性表、存储结构
有序线性表既可以采用顺序存储结构,也可以采用链式存储结构
1️⃣ 栈 vs 队列:谁“吃”谁先出来
口诀:栈吃薯片,队列排队厕。
-
栈(Stack):像吃薯片,最后一片放罐口,最先被你拿出来 → 后进先出(LIFO)。
-
队列(Queue):像排队上厕所,谁先到谁先上 → 先进先出(FIFO)。
支持子程序调用的数据结构 = 栈(Stack)
2️⃣ 循环队列:其实还是“一条线”
口诀:循环队列是“圈圈线”,不是树杈杈。
-
不管它头尾怎么绕圈圈,逻辑上仍是一条直线,每个元素只有唯一前驱、唯一后继。
-
因此属于线性结构,不是非线性。
3️⃣ 有序线性表:住“单间”还是住“套房”都行
口诀:有序表像行李,既能码整齐(顺序),也能挂成串(链式)。
-
逻辑上有序 ≠ 物理上有序。
-
你可以把元素按大小顺序连续码在数组里(顺序存储),也可以用指针把它们串成糖葫芦(链式存储)。
总结 4 个字:
“薯排圈序”
薯(栈像薯片)→ 后进先出
排(队列像排队)→ 先进先出
圈(循环队列)→ 仍是线性
序(有序表)→ 顺序链式都能住
✅“在 switch 语句中,不一定使用 break 语句。”
1️⃣ switch 用法口诀(5 秒记住)
“看表达,找标号,遇 break 跳出壳;没有 break 就往下落,default 是备胎,可写可不写。”
2️⃣ 4 句话拆讲
规则 | 举例 | 说明 |
---|---|---|
① 表达式必须是整型/字符型 | switch(n){ … } | float、string 不行 |
② case 只是入口标号 | case 1: … | 找到入口后,从此开始顺序往下执行 |
③ break 负责“跳出” | break; | 没写 break 就继续执行下一个 case(穿透) |
④ default 可省略 | default: … | 所有 case 不匹配才走这里,不是必须 |
3️⃣ 穿透示例(没有 break 也合法)
int x = 1;
switch (x) {case 1: printf("A"); // 打印 Acase 2: printf("B"); // 继续打印 Bdefault: printf("C"); // 继续打印 C
}
输出:ABC
→ 证明 break 不一定要写,因此 A 正确。
✅E-R图(Entity–Relationship 中文:实体-联系)
E-R 图就是“实体-联系图”,数据库里的“草图”:
把现实世界的人、物、事画成方块、菱形、椭圆,再连上线,
让后面写表结构的人一眼看懂“谁该建表、谁该外键”。
1️⃣ 3 个积木 + 1 条线
图形 | 颜色 | 叫什么 | 放什么 |
---|---|---|---|
⬜ 矩形 | 实体 | “东西”(学生、商品、部门) | |
♦️ 菱形 | 联系 | “动作/关系”(选课、购买、属于) | |
🔵 椭圆 | 属性 | “字段”(学号、姓名、价格) | |
—— 线 | 连线 | 把实体、联系、属性栓在一起,再标数字(1 对 1 / 1 对多 / 多对多) |
2️⃣ 10 秒草图示例
[学生] ──<选课>── [课程]│ │学号 课号姓名 课名
-
矩形:学生、课程
-
菱形:选课
-
椭圆:学号、姓名、课号、课名(挂在各自实体上)
-
连线两端写 m : n → 多对多(一名学生选多门,一门被多名学生选)
3️⃣ 后续干嘛
E-R 图 → 转成关系模式(表)
-
实体 → 表
-
属性 → 字段
-
联系 → 外键 或 新表
✅数据库应用系统的核心问题(国二常考)
数据库设计 (单选题出现“核心”二字直接秒选)
四项工作对比(盖楼模型记忆)
书面名称 | 通俗比喻 | 职责范围 | 是否核心 | |
---|---|---|---|---|
数据库设计 | 户型图 | 存什么、如何关联、字段类型、主外键、范式 | ★核心:决定系统性能与扩展性 | |
数据库系统设计 | 施工蓝图 | 选 DBMS、服务器架构、集群、分库分表 | 重要,但为“实现”服务 | |
数据库维护 | 物业保洁 | 备份、恢复、升级、监控、索引重建 | 运维阶段,非核心 | |
数据库管理员培训 | 发使用手册 | 培训 DBA 操作与应急 | 人的因素,与系统核心 |
✅需求分析阶段用DFD图【软件设计阶段不使用】
需求分析阶段(做什么)
图名 | 全称 | 通俗解释 | 记忆口诀 |
---|---|---|---|
DFD图 | 数据流图 | 描述数据怎么流动,像“快递路线” | “D”=Data(数据),F=Flow(流),D=Diagram(图) → “数据流图”就是数据快递图 |
设计阶段(怎么做)
图名 | 全称 | 通俗解释 | 记忆口诀 |
---|---|---|---|
N-S图 | Nassi-Shneiderman图 | 像“积木块”一样的流程图,没有箭头,结构清晰 | “N-S”=“No箭头-Structured” → “无箭头结构化图” |
PAD图 | Problem Analysis Diagram | 像“树状图”,从左到右分叉,表示程序结构 | “PAD”=“分叉树” → “PAD=分叉图” |
程序流程图 | Program Flow Chart | 传统流程图,有箭头、有判断、有循环 | “箭头满天飞” → “程序流程图=箭头图” |
一句话总结(终极口诀):
“需求DFD画数据,设计N-S/PAD画结构,流程图写代码。”
实战记忆技巧(联想记忆):
-
DFD:想象你在画**“快递路线图”**,数据就是包裹,从A系统送到B系统。
-
N-S图:像**“乐高积木”**,一块一块搭起来,没有箭头,但结构清楚。
-
PAD图:像**“家谱图”**,从左到右分叉,主干-分支-叶子。
-
程序流程图:像**“地铁线路图”**,箭头指方向,有分叉(判断)、有环线(循环)。
总结表格(速记版):
图名 | 阶段 | 作用 | 口诀 |
---|---|---|---|
DFD图 | 需求分析 | 数据流动 | “数据快递图” |
N-S图 | 设计 | 结构化程序 | “无箭头积木图” |
PAD图 | 设计 | 程序结构 | “分叉树图” |
程序流程图 | 编码 | 算法逻辑 | “箭头满天飞图” |
✅数据管理技术发展的三个阶段中,数据共享最好的是:数据库系统阶段。
三个阶段对比速览:
阶段 | 数据共享程度 | 原因说明 |
---|---|---|
人工管理阶段 | ❌ 极差 | 数据完全嵌入程序中,无专门管理软件,程序之间无法共享数据。 |
文件系统阶段 | ⚠️ 有限 | 有文件系统管理数据,但数据仍按应用划分,共享困难,冗余严重。 |
数据库系统阶段 | ✅ 最好 | 使用DBMS统一管理,数据独立、结构化,支持多用户、多应用共享,冗余最小。 |
一句话总结(记忆口诀):
“人工不共享,文件难共享,数据库好共享。”
✅115L能用作数据常量
表示115是长整型数据
✅逗号表达式,整个逗号表达式为第二个表达式的值
设有定义:intx=2;,以下表达式中值不为6的是( )。
A:2*x,x+=2
B:x++,2*x
C:x*=(1 +x)
D:x*=x+ 1//A错,因为逗号表达式运算结果等于第二个表达式
✅赋值运算符的优先级高于逗号运算符
#include <stdio.h>main()
{int x,y,z;x=y=1,z=x++,y++,++y;printf("%d, %d,%d",x,y,z)
}//程序运行后结果
//2,3,1
✅国二选择题挖坑汇总:
switch后加 ; //无法运行
*p=NULL //把指针初始化为 空指针(0 地址)