--- title: "05-C语言笔试" created: 2026-01-04 tags: - 博客 --- # C语言笔试 ## 考试时间 ## 题型分布 一、选择题(共20小题,每小题1分,共20分) 二、填空题(共10空,每空1分,共10分) 三、判断题(共10小题,每小题1分,共10分) 四、程序阅读题(共5小题,每小题4分,共20分) 五、算法设计题(共2小题,每小题8分,共16分) 六、程序设计题(共2小题,每小题12分,共24分) 按知识点一个一个过关 ## 选填判(小题) ### 按知识点学习: #### 基础知识 程序是什么(目的): 程序是为实现特定目标或解决特定问题而用计算机语言(程序设计语言)编写的一系列语句或指令,计算机会严格执行这些指令。 C语言中,**语句**是程序最基本的执行单位,以分号结尾。 函数 语句 main函数是什么: 所有c程序有且只有一个main函数,c程序总是从main()函数的第一条语句开始执行,当main()函数结束执行时,程序结束执行。 任何程序的结构都可以通过3种基本结构进行组合来实现,分别是 **顺序结构**、**选择结构**和**循环结构**。 #### 标识符 c语言的标识符由字母、数字和下划线组成,其中第一个字符必须是字母或下划线。 注意还有一点 不能是c语言已有的关键字 但这题主要考察的是能不能以数字开头 记住:不能 有时候看见下划线开头会感觉奇怪 但其实是可以的 这题应该会以选择题的形式出现 会做这几道就没问题 掌握了 ![[image-617c0155.png]] - 练习: - ![[image-bf2ef698.png]] - ![[image-f203927f.png]] - ![[image-37bd00da.png]] - ![[image-92d83c0b.png]] - ![[image-535a3d27.png]] - ![[image-370bf651.png]] #### 数据类型 在 C 语言中,**数据类型通常分为两大类**: 1. **基本数据类型(分为**整型、字符型、实型(浮点型)) 用于表示最基本的数据,包括: - `int`(整型) - `float`(单精度浮点型) - `double`(双精度浮点型) - `char`(字符型) 2. **构造数据类型** 由基本数据类型组合或派生而来,包括: - 数组(array) - 结构体(struct) - 共用体(union) - 枚举(enum) - 指针(pointer) 考察形式应该为填空题 C 语言的数据类型分为**基本数据类型**和**构造数据类型**两类。 C 语言的**基本数据类型分**为**整型**、**字符型**、**实型(浮点型)**三类。 `unsigned`:无符号(没有负数) **特性:** 只能存储非负整数(0 和正数)。因为不需要存符号位,正数范围比同级有符号类型大一倍。 - **考察点:** 负数变大数、正数翻倍。 - **含义:** 只能存 0 和正数。 - **好处:** 正数范围扩大一倍(比如 `char` 最大 127,`unsigned char` 最大 255)。 - **输入输出:** `%u`。 - **下溢/回绕 (Underflow):** - **无符号数永远不会是负数。** - 如果你写 `unsigned short a = 0; a = a - 1;` 试图计算 `0 - 1` 时,它不会变成 `-1`,而是会“倒转”回该类型能表示的**最大值**。 - `unsigned short` (范围 0~65535)。`0 - 1` = `65535`。 - *类比:* 就像机械里程表,从 `0000` 往回拨一格,会变成 `9999`。 - 练习: - ![[image-0d4a519a.png]] - ![[image-28168d29.png]] - ![[image-31f226b7.png]] `long long` **(超长整型):** - **用途:** 当标准 `int` (通常 ±21亿左右) 不够存时使用(如阶乘、斐波那契数列)。 - **内存:** 标准规定至少 64 位,通常占用 **8 个字节**。 - **格式控制符:** 输入输出必须使用 `%lld`。`scanf("%lld", &a);` `printf("%lld", a);` 如果误用 `%d`,会导致数据截断或打印出错误的负数/乱码。 - 练习 - ![[image-cde7e06f.png]] - ![[image-72443a94.png]] - ![[image-d63adcf0.png]] - ![[image-20cedf2c.png]] 不过很大可能只是简单的出一道填空题 问: `unsigned`使用**%u**格式化输出,`long long`使用**%lld**格式化输出。 ##### sizeof 表达式`sizeof()` 字符(char)是 **1**,短整(short)是 **2**。 整型(int)、单浮点(float) 都是 **4**。 双浮点(double) 是 **8**。 #### 常量 ##### 字符常量 **知识点1:** `'A'` 的 ASCII 值是 **65**(以此类推 `'B'`=66)。 `'a'` 的 ASCII 值是 **97**(以此类推 `'b'`=**98**)。 `'0'` 的 ASCII 值是 **48**。 **转换规律:** 小写字母 = 大写字母 + 32。 **怎么考:**题目通常会问 `char c = 'A' + 32;` 输出什么(答案是 'a'),或者问 `printf("%d", 'b');` 输出什么(答案是 98)。 **知识点2:** 区分 **字符 '4'** 和 **数字 4**。 **数字 4**:就是整数 4,在内存里存的就是二进制的 4。 **字符 '4'**:是一个符号,在内存里存的是它的 ASCII 码。已知 `'0'`=48,那么 `'4'` = 48 + 4 = **52**。 **怎么考:**让你计算 `'4' - '0'` 的结果(答案是整数 4),或者问 `printf("%d", '4');` 输出什么(答案是 52,绝对不是 4)。 **知识点3:** `printf` 的格式控制符 `%e`。 **知识点:** `%e` 用来以**指数形式(科学计数法)**输出浮点数。 **怎么考:** 给你一个数 `123.456`,问用 `%e` 输出长什么样(类似 `1.234560e+002`)。 - **练习:** - ![[image-ca0e7731.png]] - ![[image-c34e8066.png]] - ![[image-6f718743.png]] ##### 字符串常量 字符串常量是由一对双引号“”括起来的零个或多个字符序列,如“HelloWorld"等,默认以‘\0’作为结束标志,因此由n个字符组成的字符串在内存中需要占用n+1个字节的空间。 考点核心在于区分 **“字符串长度”** 和 **“占用内存大小”**。 对于字符串 `"Hello\0World"`,编译器在内存里是这样存的: | **H** | **e** | **l** | **l** | **o** | **\0** | **W** | **o** | **r** | **l** | **d** | **\0** | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 1 | 2 | 3 | 4 | 5 | **6** | 7 | 8 | 9 | 10 | 11 | **12** | 注意最后那个隐形的 `\0`,这是 C 语言自动帮你加的字符串结束符。 **考法 A:问长度 (strlen)** 规则: strlen 函数是个“老实人”,它从头开始数,一旦遇到第一个 \0 就立马停工,后面的东西它全都不看。 结果: 它只数到 'o' 就停了。 答案: 长度为 5。 **考法 B:问占用空间 (sizeof)** 规则: sizeof 关注的是“房子有多大”。它不管你中间有没有结束符,它只管这一坨东西在内存里实打实占了几个格子。 计算: "Hello" (5个) + \0 (1个) + "World" (5个) + 最后的隐形 \0 (1个)。 答案: 占用字节数为 12。 - 练习: - ![[image-e010c245.png]] - ![[image-3b96d5cd.png]] - ![[image-4e9b87ca.png]] - ![[image-e3d29aa0.png]] - ![[image-6ea71c3c.png]] ##### 符号常量 在C语言中,可以用一个标识符来表示一个常量,称为符号常量,一般形式为 #define 标识符 常量 **注意!!! 它是****直接替换** 考点 **1、千万别加分号!** ❌ 错:`#define PI 3.14;` ✅ 对:`#define PI 3.14` **为什么?** 它是“傻瓜式替换”。如果你加了分号,代码里写 `x = PI * 2;` 就会变成 `x = 3.14; * 2;`,直接报错! **2、没有“=”号** ❌ 错:`#define N = 100` ✅ 对:`#define N 100` 它不是赋值变量,中间用空格隔开。 3、原样替换(运算优先级陷阱) 高分题的分水岭。 **考题:** `#define M 2+3`,问 `M * 2` 是多少? **直觉(错):** (2+3) \* 2 = 10。 **真相(对):** 它是原样拼凑!变成了 `2+3 * 2`。根据优先级先算乘法,结果是 `2 + 6 = 8`。 - **练习:** - ![[image-ac0a0a00.png]] - ![[image-30abfe31.png]] - ![[image-59566f7a.png]] #### 运算符 ##### 优先级 1. 核心概念 在 C 语言中,一个表达式不是从左到右随便算的,而是由**运算符优先级**和**结合性**共同决定计算顺序。 - **优先级 (Precedence)**:决定“谁先算”。优先级高的运算符先结合(例如乘除先于加减)。 - **结合性 (Associativity)**:当优先级**相同**的时候,决定“怎么算”。 - **左结合**:从左向右算(大多数运算符,如 `a - b + c`)。 - **右结合**:从右向左算(主要是**单目运算符**、**赋值运算符**和**三目运算符**)。 2. 常见运算符优先级速查表(高 → 低) | 优先级 | 运算符 | 说明 | 结合性 | | --- | --- | --- | --- | | **最高** | `!`, `++`, `--`, `(type)`, `sizeof` | **单目运算符** (逻辑非, 自增, 强转等) | **右到左** | | ↓ | `*`, `/`, `%` | 算术运算 (乘, 除, 取余) | 左到右 | | ↓ | `+`, `-` | 算术运算 (加, 减) | 左到右 | | ↓ | `<`, `<=`, `>`, `>=` | 关系运算 (大小比较) | 左到右 | | ↓ | `==`, `!=` | 关系运算 (相等性判断) | 左到右 | | ↓ | `&&` | 逻辑与 | 左到右 | | ↓ | `||` | 逻辑或 | 左到右 | | **最低** | `=` | 赋值运算 | **右到左** | 3. 短路求值 > 在逻辑表达式中,**如果在计算过程中,表达式的最终真假已经可以确定,那么剩余的子表达式将不会再被求值。** 在 C 语言中,**只有两个运算符具有短路行为**: | 运算符 | 名称 | | --- | --- | | `&&` | 逻辑与 | | `||` | 逻辑或 | ①:逻辑与 `&&` ``` A && B ``` 若 A == 0(假) 整个表达式结果已确定为假 B 不再计算 ➡️ **左假即停** **②:**逻辑或 `||` ``` A || B ``` - 若 `A != 0`(真) - 整个表达式结果已确定为真 - **B 不再计算** ➡️ **左真即停** 短路的执行顺序(非常关键) (1) 固定从左到右 - `&&` 和 `||` **一定从左向右求值** - 不允许交换顺序 - 不允许并行计算 (2)与“优先级”的关系 > **优先级决定表达式如何分组** > **短路决定分组后的子表达式是否继续计算** 两者是**不同层面**的规则,不能混为一谈。 - 练习 - ![[image-7ac35e2b.png]] - ![[image-e3f7c79c.png]] - ![[image-0fb95bc6.png]] - ![[image-2354a140.png]] - ![[image-5a937872.png]] 问题: ``` a=5; b=4; int k = a > b; k = ? ``` ``` a>b -> 5>4 -> true -> 1 k=1 ``` ##### 逻辑运算(真假与数值的关系) 1. C 语言中的“真假”规则 C 语言并没有专门的 `bool`(C99 之前),逻辑判断遵循: - **0 → 假(false)** - **非 0 → 真(true)** 这是理解所有逻辑表达式的基础。 2. 常见逻辑运算符含义 | 运算符 | 含义 | 结果 | | --- | --- | --- | | `!a` | 逻辑非 | 真→0,假→1 | | `a && b` | 逻辑与 | 同真才真 | | `a || b` | 逻辑或 | 同假才假 | 注意: - **逻辑运算的结果只能是 0 或 1** - 即使参与运算的是任意整数,结果仍被“压缩”为真假值 - 练习 - ![[image-574053ff.png]] - ![[image-ad15a8e9.png]] - ![[image-19648bac.png]] - ![[image-07c22480.png]] - ![[image-1d8e6bc7.png]] ##### 逻辑运算符和逻辑表达式(短路是拔尖题 必考) ! && || - **优先级阶梯:** - `!` **(非)** > **算术运算符** (+ - \* /) > **关系运算符** (> < ==) > `&&` **(与)** > `||` **(或)** > **赋值** (=) - **口诀:** “非”最高,“与”比“或”大。 - **例子:** `a || b && c` 等价于 `a || (b && c)`。 - **真假的定义:** - **输入时:** 非 0 即为真,0 为假。(例如:`-5` 是真,`3.14` 是真,只有 `0` 是假)。 - **输出时:** 逻辑运算的结果只有两个值:**1 (真)** 或 **0 (假)**。 - `5 && 4` 的结果是 `1`,绝不是 `4` 或 `20`! - **【核心难点】短路法则(懒人法则):** - `&&` **(与) 的短路:** 如果左边是 **假 (0)**,右边**完全不执行**。(因为一假则假,没必要看了)。 - `||` **(或) 的短路:** 如果左边是 **真 (非0)**,右边**完全不执行**。(因为一真则真,没必要看了)。 典型例题分析(短路陷阱) ``` int a = 0, b = 1; if (a++ && b++) { // ... } printf("a=%d, b=%d", a, b); ``` - **分析:** 1. 执行 `a++ && ...`。由于是后缀 `++`,先取 `a` 的旧值 **0** 参与逻辑运算。 2. `0` 是假。对于 `&&` 来说,左边为假,整体必为假。**触发短路!** 3. **后果:** 右边的 `b++` **直接被跳过,根本不执行**。 4. 之后 `a` 完成自增变为 1。 - **结果:** `a=1`, `b=1` (b 没变)。 - 练习题: - ![[image-1f63881b.png]] - ![[image-e3b23cf6.png]] - ![[image-34b26f04.png]] **一句话总结:** 逻辑题核心看 **“短路”**:`&&` 遇假就停,`||` 遇真就停,后面有 `i++` 也别管它! 填空题: ``` int x=33,y=4; !x+x/y=? ``` ``` !33 → 0 0 + x / y 33 / 4 → 8 0 + 8 0 + 8 = 8 结果为 8 ``` ##### 自增自减运算符 (可能考程序阅读也可能考小题 挺重要的 一定要掌握) 不管题目多花哨,记住这两个原则: - `++i`**(前缀):先变,后用。** - (先把自己的值 +1,然后再拿去参加运算/赋值)。 - `i++`**(后缀):先用,后变。** - (先拿现在的旧值去参加运算/赋值,等这一行代码执行完了,自己再偷偷 +1)。 骗局 A:赋值时的差异 ``` int a = 3; int b = a++; // 过程:b 先拿走 a 的旧值 3,然后 a 自己变成 4。 // 结果:b=3, a=4 ``` VS ``` int a = 3; int b = ++a; // 过程:a 先变成 4,然后 b 拿走这个新值 4。 // 结果:b=4, a=4 ``` 骗局 B:作为循环条件(高频考点) ``` int k = 0; while (k++ < 3); printf("%d", k); ``` **分析:** 很容易会填 3。**错!答案是 4。** - 最后一次判断时,`3 < 3` 不成立,循环结束。但在判断的那一瞬间,`k++` 已经执行了(先判断,判断完立刻自增)。所以 k 变成了 4。 遇到 `while(i++ < n)`,记得判断完的时候,`i` 已经加了 1,进门(循环体)的是大一号的 `i`。 - 练习: - ![[image-87864fdb.png]] - ![[image-fba31251.png]] - ![[image-72394776.png]] - ![[image-471c7c65.png]] - ![[image-410ef570.png]] 填空题: ``` int a=5; int b,c; b=++a; c=a++; a, b, c的值最终是多少? ``` ``` ++a a=6 b=a=6 c=a=6 a++ a=7 a=7 b=6 c=6 ``` ##### ##### ##### 条件运算符 三目表达式 ? : 核心公式(死记硬背) C ``` 表达式1 ? 表达式2 : 表达式3 ``` - “真 1 挑 2,假 1 挑 3”。 - **翻译:** - 先算 `表达式1`(是不是真?)。 - **真 (非0)**:结果就是 `表达式2` 的值。 - **假 (0)**:结果就是 `表达式3` 的值。 经典考法:求最大值 这是最基础的考法,必须秒杀。 C ``` max = (a > b) ? a : b; ``` - **意思:** 如果 a 大于 b,就把 a 给 max;否则把 b 给 max。 - **等价于:** ``` if (a > b) max = a; else max = b; ``` 【高阶陷阱】右结合性(连环嵌套) 这是选择题里最爱用来绕晕你的考法。 **例题:** `a > b ? a : c > d ? c : d` - **解题秘籍:** 遇到连续的 `? :`,**从右往左** 或者是 **剥洋葱** 式的看。 - **正确断句:** `a > b ? a : (c > d ? c : d)` - 先看最外层:`a > b` 成立吗? - 成立 -> 直接取 `a`,游戏结束。 - 不成立 -> 跑去执行括号里的 `(c > d ? c : d)`。 - 练习 - ![[image-91d80328.png]] - ![[image-c0418a4b.png]] - ![[image-885abcfa.png]] 看见 `?` 就问自己“是真的吗?”,真就拿走冒号**前面**的,假就拿走**后面**的。遇到一长串,先把**最右边**的括号括起来看。 #### 输入输出 ##### 基本输入输出函数 1. 输出函数:`printf` `printf("格式字符串", 参数列表);` - **作用**:把数据输出到屏幕 - **格式字符串**决定“怎么显示” - **参数列表**决定“显示什么值” ⚠️ **参数个数必须 ≥ 格式符个数** ``` printf("%d", a, b); // 只输出 a,b 被忽略 ``` 2. 输入函数:`scanf` `scanf("格式字符串", 地址列表);` - **作用**:从键盘读入数据,存入变量 - **本质**:`scanf` 要“往变量里写数据” ##### scanf 的核心规则 1. 为什么要加 `&`(取地址符) **规则:** > `scanf` 往变量里存数据,必须知道变量住在哪里(内存地址)。 `int a; scanf("%d", &a); // 正确` 2. 必须加 `&` 的情况 - **普通变量**:`int / float / char / double` `scanf("%d", &a); // ✅` 3. 不用加 `&` 的特殊情况(了解) - **数组名** - **指针变量本身** 4. 错题典范 ``` scanf("%d", a); // ❌ 错误:a 不是地址 scanf("%d", &a); // ✅ 正确 ``` ##### 常用格式控制符(输入 + 输出) | 格式符 | 含义 | 说明 | | --- | --- | --- | | `%d` | 十进制整数 | 最常用 | | `%x` | 十六进制整数(小写) | 输出 a–f | | `%X` | 十六进制整数(大写) | 输出 A–F | | `%o` | 八进制整数 | | | `%f` | 浮点数 | 默认 6 位小数 | | `%c` | 字符 | | | `%s` | 字符串 | | ⚠️ **重点理解**: > **格式符决定“如何解释和显示数据”,不改变变量本身的值** 示例:`%x / %X` ``` int a = 10; printf("%x", a); // a printf("%X", a); // A ``` - 变量里存的还是 **十进制 10** - `%x / %X` 只是**输出时按十六进制显示** ##### printf 的输出宽度与对齐 > **正数宽度:右对齐,左补空格** > **负数宽度:左对齐,右补空格** --- 假设: ``` int a = 123; ``` 常见格式化示例 | 格式 | 含义 | 输出效果(□ 表示空格) | 记忆要点 | | --- | --- | --- | --- | | `%5d` | 宽度 5,右对齐 | □□123 | 正数 → 右对齐 | | `%-5d` | 宽度 5,左对齐 | 123□□ | 负号表示左对齐 | | `%2d` | 宽度 2(不够) | 123 | 不截断 | | `%05d` | 宽度 5,补 0 | 00123 | 0 表示补零 | | `%8.2f` | 宽度 8,小数 2 位 | □□□□3.14 | 默认右对齐 | | `%-8.2f` | 宽度 8,小数 2 位 | 3.14□□□□ | 负号左对齐 | | `%4.2f` | 宽度 4,小数 2 位 | 3.14 | 刚好 | | `%3.2f` | 宽度 3,不够 | 3.14 | 不截断 | | `%08.2f` | 宽度 8,补 0 | 00003.14 | 0 表示补零 | 宽度的本质 - **宽度是“最小输出宽度”** - 如果数据本身更宽: - **直接原样输出** - **不会截断** 精度的本质(浮点数特有) 1. 精度控制的是“小数位数” ``` vdouble x = 3.14159; printf("%.2f", x); // 3.14 发生 四舍五入 ``` - 不只是简单截断 2. 精度优先于宽度 `printf("%4.1f", 12.34); // 12.3` - 先按精度算结果 - 再检查宽度是否足够 ##### 格式字符串与参数的对应关系 规则 > **一个格式符,对应一个参数** ``` printf("%d %d", a, b); // 输出两个整数 printf("%d", a, b); // 只输出 a ``` ⚠️ **多给参数不会报错,但没用** ##### scanf 中格式符的注意点 1. `%d` 与输入形式 `scanf("%d", &a);` - 自动跳过空白符(空格、回车、Tab) - 读取一个十进制整数 2. `%d` ≠ `-%d` - `-` **不是格式符** - `%-5d` 中的 `-` 是 **printf 的对齐控制** - 在 `scanf` 中没有“左对齐/右对齐”的概念 - ##### **练习:** - ![[image-ca7e4616.png]] - ![[image-e184c07c.png]] - ![[image-d6454a01.png]] - ![[image-c833911c.png]] - ![[image-98863cb0.png]] - ![[image-cb535e7a.png]] - ![[image-b560d1bc.png]] - ![[image-09e2f833.png]] - ![[image-d47d93d7.png]] - ![[image-1b9fc7fd.png]] - ![[image-c43de94f.png]] - ![[image-c4a1912c.png]] - ![[image-48fe4ab5.png]] 问题1: ``` 格式修饰符都有哪些? %d, %3d, %u, %x, %#x, %ld, %f, %lf, %4.2f, %c, %s的含义分别是什么? printf(“%c,%d”, ‘A’,’A’); ``` ``` %d 含义:输出十进制有符号整数(int) %3d 含义:输出宽度至少为 3 的十进制整数,右对齐 若数字不足 3 位,左边补空格 若超过 3 位,直接原样输出 %u 含义:输出无符号十进制整数(unsigned int) 同一块内存,按“无符号”方式解释 %x 含义:以十六进制输出整数(小写字母 a–f) 仅影响显示,不改变变量的值 %#x 含义:以十六进制输出,并自动加前缀 0x 字母仍为小写 %ld 含义:输出 long int 类型的十进制整数 用于 long 型整数 %f 含义:输出浮点数(float / double) 默认 保留 6 位小数 %lf 含义: printf 中:等价于 %f scanf 中:表示读入 double %4.2f 含义: 总宽度至少为 4 小数点后保留 2 位 默认右对齐 %c 含义:输出一个字符 对应 char 类型或字符常量 %s 含义:输出字符串(以 \0 结尾的字符数组) 不输出 \0 printf("%c,%d", 'A', 'A'); %c 'A' 作为字符输出 输出字符:A %d 'A' 实际是字符常量 在 C 语言中,字符本质是整数(ASCII 码) 'A' 的 ASCII 值是 65 实际输出结果: A,65 ``` 问题2: ``` printf函数调用时,格式说明符数量与实际提供的参数数量不一致时会发生什么? ``` ``` 1. 格式说明符多于参数 int a = 10; printf("%d %d", a); %d 有两个,但只提供了一个参数 第二个 %d 没有对应参数 结果: 输出第一个数字正确(10) 第二个数字随机、可能乱码、甚至导致段错误 本质原因:printf 依赖栈访问参数,如果没提供就读取随机栈值 2. 格式说明符少于参数 int a = 10, b = 20; printf("%d", a, b); %d 只有一个说明符,但提供了两个参数 结果: 只使用第一个参数 a b 被忽略 不会报错,也不会打印 b 3. 参数类型与格式符不匹配 double x = 3.14; printf("%d", x); %d 期望 int,实际传入 double 结果: 输出不可预测 可能出现乱码或错误值 这是 典型未定义行为(Undefined Behavior) ``` | 情况 | 行为 | 备注 | | --- | --- | --- | | 格式符 > 参数 | 随机值 / 崩溃 | 没有对应参数会访问“垃圾栈” | | 格式符 < 参数 | 多余参数忽略 | 不会报错 | | 类型不匹配 | 未定义行为 | 输出不可预测 | #### 字符型数据的输入和输出 ##### `putchar`:专一的输出者 - **全名:** put character(输出字符)。 - **功能:** 向屏幕输出**一个**字符。 - **写法:** `putchar(参数)` - 参数可以是字符常量:`putchar('A')` - 参数可以是变量:`putchar(c)` - 参数可以是整型(ASCII):`putchar(65)` (输出 'A') - 参数可以是转义字符:`putchar('\n')` (换行) - **考点:** 它**只能**输出一个字符,不能输出字符串(不能写 `putchar("ABC")`)。等价于 `printf("%c", c)`。 ##### `getchar`:来者不拒的接收者 - **全名:** get character(获取字符)。 - **功能:** 从键盘读入**一个**字符。 - **写法:** `变量 = getchar();` (注意:括号里没参数!) - **【超级死穴】回车符陷阱:** - `getchar()` 是个“老实人”,它连你按下的 **回车键 (Enter)** 都会当成一个字符 `'\n'` 读进去! - **场景:** 考试程序让你输入 "A",然后按回车。 - 如果你写了两次 `getchar()`: - 第1次读到:`'A'` - 第2次读到:`'\n'` (换行符) —— **容易以为还是读下一个字母,错!** - **练习题:** - ![[image-d839cce9.png]] - ![[image-3cee7357.png]] - ![[image-bf3a783f.png]] #### 数学库函数 **在头文件math.h中** 指数 exp() 绝对值 fabs() 对数 log() 幂函数 pow() 平方根 sqrt() - **绝对值**: - `abs(int x)`:求**整数**的绝对值(其实它在 `` 里,但考试常混考)。 - `fabs(double x)`:求**浮点数**(小数)的绝对值(这才是 `` 的亲儿子)。 - **考点:** 看到 `fabs(-3)` 别选 `3`,要选 `3.0`(因为返回值是 double)。 - `pow(x, y)` **幂函数**: - 表示 \(x^y\)(x 的 y 次方)。 - **考点:** 两个参数都是 **double**,返回值也是 **double**。 - **坑:** `pow(2, 3)` 的结果不是整数 `8`,而是浮点数 `8.0`。 - `sqrt(x)` **平方根**: - **考点:** 参数必须是**非负数**。`sqrt(-4)` 会导致运算错误(Domain error)。返回值是 **double**。 - `log(x)` **对数**: - **考点:** C 语言里的 `log(x)` 特指**自然对数**\(\ln(x)\)(以 \(e\) 为底)。 - 如果考试让你算 \(\lg(x)\)(以 10 为底),必须用 `log10(x)`。这是一大坑点! 要调用数学函数时,在#include命令行应该包含 **math.h** - **练习题:** - ![[image-b2b26806.png]] - ![[image-731d4aac.png]] - ![[image-59ab5fff.png]] #### 分支结构 if else switch case 注意if()后面加个; 里面不执行 ##### `if` 语句的“三大坑” 坑一:夺命分号 C ``` if (a > 5); // <-- 注意这里!有个分号 printf("大"); ``` - **后果:** 这里的 `;` 代表“空语句”。意思是:如果 a>5,什么都不做。 - **现象:** 下面的 `printf("大")` 不再受 if 控制,**无论 a 是多少,它都会打印**。 - **口诀:** `if` 后面别加分号,除非你想让它空转。 坑二:赋值与等于混淆 - ❌ 错:`if (a = 5)` —— 这是把 5 赋值给 a,表达式结果为 5(真),**永远执行**。 - ✅ 对:`if (a == 5)` —— 这才是判断相等。 坑三:悬空 `else`(配对原则) C ``` if (a > 0) if (b > 0) printf("A"); else printf("B"); // 这个 else 属于谁? ``` - **规则:** `else` 总是跟**最近的、还没配对的**那个 `if` 结合。 - **真相:** 这个 `else` 是属于 `if(b>0)` 的,尽管缩进故意骗你它属于第一个。 ##### `switch` 语句的“连环坑” 坑一:穿透现象(漏写 break) C ``` switch(a) { case 1: printf("A"); // 没写 break,继续往下跑! case 2: printf("B"); break; } ``` - **结果:** 如果 a 是 1,它会**先打印 A,再打印 B**,直到遇到 break 才停。这叫“穿透”。 坑二:类型限制 - `switch(x)` 括号里的 x **只能是整型(int, long等)或字符型(char)**。 - ❌ **绝对不能是:** `float`, `double`(小数不能精确比较),或者字符串。 - ##### 练习: - ![[image-5dee81f9.png]] - ![[image-6c862a81.png]] - ![[image-c37af5b2.png]] - ![[image-bd62b477.png]] 问题1: ``` if (条件) { a = 1; b = 2; } if (条件) a = 1; b = 2; if (条件) a = 1, b =2; if (条件); a = 1; b = 2; ``` ``` 注意区分 看准括号和分号的位置 ``` 问题2: ``` switch语句中,各个case和default的出现次序影响执行结果吗? ``` ![[image-27180752.png]] #### 循环结构 ##### for 循环 1. 语法 ``` for (初始化; 条件; 更新) { // 循环体 } ``` - **初始化**:循环变量初始值,只执行一次 - **条件**:循环执行的判断条件(真/假) - **更新**:每次循环结束后执行(通常自增/自减) 2. 执行流程 1. 执行 **初始化** 2. 判断 **条件** - 真 → 执行循环体 - 假 → 结束循环 3. 执行 **循环体** 4. 执行 **更新** 5. 回到 **条件判断** 6. 重复步骤 2-5,直到条件为假 3. 特点 - **循环次数通常确定或可控** - **循环变量在循环外可用** - 适合 **计数循环**(for i=0;i` (考试填空常考) - **功能:** 按照 **ASCII 码值**,从左到右一个个字符比较两个字符串。 - **原型:** `int strcmp(const char *s1, const char *s2);` 2. 三个返回值 `n = strcmp(s1, s2);` | **返回值** | **含义** | **记忆方法** | | --- | --- | --- | | **0** | **两个字符串完全相同** | 0 代表“无差别” | | **> 0** (正数) | **s1 大于 s2** | s1 赢了(ASCII码更大) | | **< 0** (负数) | **s1 小于 s2** | s1 输了(ASCII码更小) | **怎么比大小?** - 像查字典一样。 - `"abc"` 和 `"abd"`:前两个一样,比第三个。`'c'`(99) 小于 `'d'`(100),所以结果是**负数**。 - `"abc"` 和 `"abcd"`:前三个一样,第四个位置 `'\0'`(0) 小于 `'d'`(100),所以前者小,结果是**负数**。 3. `if` 判断陷阱 **❌ 错误写法:** ``` // 很容易以为 strcmp 返回 1 代表相等,大错特错! if (strcmp(s1, s2)) { printf("相等"); } ``` C ``` // 很多初学者以为 strcmp 返回 1 代表相等,这是大错特错! if (strcmp(s1, s2)) { printf("相等"); } ``` **解析:** 如果 `s1` 和 `s2` 真的相等,`strcmp` 返回 **0**。在 C 语言中,**0 代表假**。所以上面的代码**根本不会执行** `printf`。 **✅ 正确写法:** ``` if (strcmp(s1, s2) == 0) { printf("相等"); } ``` - 练习: - ![[image-fae0ef71.png]] - ![[image-2e6d0882.png]] - ![[image-03e82a37.png]] 想要判断相等,必须写 `== 0`;如果不写 `== 0`,那它实际上是在判断“这两个字符串不一样”。 C ``` if (strcmp(s1, s2) == 0) { printf("相等"); } ``` 问题: ``` 如何通过strcmp函数比较两个字符串? ``` ``` strcmp 函数定义在 头文件中,用于 比较两个字符串。 函数原型: int strcmp(const char *str1, const char *str2); 返回值说明: 返回 0:两个字符串 完全相同 返回 < 0:str1 小于 str2(按 ASCII 码顺序比较) 返回 > 0:str1 大于 str2(按 ASCII 码顺序比较) 注意:strcmp 是区分大小写的。例如 "abc" 和 "ABC" 不相等。 ``` #### 函数 ##### 有参函数的定义 (P154) 定义函数就是“造工具”。你告诉编译器这个工具叫什么、能干什么、需要什么材料。 - **基本语法格式:** ``` 返回值类型 函数名( 类型 形参1, 类型 形参2, ... ) { // 函数体(要做的事情) return 结果; } ``` - **例子:** ``` int add(int x, int y) { // x, y 是接收数据的容器 return x + y; } ``` ##### 函数的返回值 (P160) - **关键字:** `return`。 - **规则:** - `return` 后面的表达式类型应与函数定义时的“返回值类型”一致。如果不一致,会自动发生类型转换(以函数定义时的类型为准)。 - 如果函数类型为 `void`,则不需要返回值,可以写 `return;` 也可以不写。 - 一旦执行到 `return`,函数立刻结束,跳回调用处。 ##### 函数声明 (P161) - **作用:** 相当于“菜单”。在正式上菜(定义函数)之前,先告诉编译器“我有这个菜”。 - **何时需要?** - 如果函数定义在 `main` 函数**之后**,必须在 `main` 之前(或调用之前)进行声明。 - 如果函数定义在 `main` 函数**之前**,声明可以省略。 - **格式:** `返回值类型 函数名(类型, 类型);` (分号不能少!) ##### 问题 Q1:什么是形参,什么是实参? - **形参 (Formal Parameter):** - **定义时**括号里的变量。 - 它是“占位符”,函数未被调用时不占内存,只有调用时才分配内存。 - *例子:* `void fun(int a)` 里的 `a`。 - **实参 (Actual Parameter):** - **调用时**括号里传递的具体数值或变量。 - 它是“实际的数据”,必须有确定的值。 - *例子:* `fun(2)` 里的 `2`。 - **关系:** 发生函数调用时,**实参的值会单向复制给形参**(值传递)。 Q2:函数调用之前是否需要先声明? - **原则上需要。** 编译器必须先认识这个函数(知道它的名字、参数类型、返回值类型),才能检查调用是否合法。 - **特例:** 如果函数的**定义**(完整代码)写在调用它的位置**之前**,那么定义本身就充当了声明,不需要额外写声明语句。 Q3:是否可以调用 main 函数? - **在 C 语言中:可以。** `main` 函数本质上也是一个函数,可以在程序中递归调用自己(虽然在实际开发中极少这样做,容易死循环)。 - *(注:在 C++ 中是禁止调用 main 函数的,但 C 语言考试通常认为可以)*。 Q4:什么是外部函数和内部函数,分别使用哪个关键字? - **内部函数 (静态函数):** - 只能被**当前文件**中的其他函数调用,不能被其他文件使用。 - 关键字:`static` - 格式:`static int fun(...)` - **外部函数:** - 可以被其他文件调用。C 语言中函数默认都是外部的。 - 关键字:`extern` (定义时通常省略,声明时可加上) Q5:当哪条语句被执行时,程序的执行流程无条件地从一个函数跳转到另一个函数? - 答案:**函数调用语句**。 - 当程序执行到 `fun();` 这种调用语句时,控制权会立即从当前函数(如 main)转移到被调用的函数(如 fun)。 - 而 `return` 语句则是将控制权**跳回**。 #### 指针与字符串 字符串与字符指针P209 - **定义方式:** - 数组方式:`char str[] = "Hello";` (存放在栈区,变量,内容可修改) - 指针方式:`char *p = "Hello";` (p 指向常量区的字符串,常量,内容**不可修改**) - **核心区别:** - `str` 是数组名,是**地址常量**,**不能**进行自增操作(如 `str++` 是非法的)。 - `p` 是指针变量,可以改变指向,**可以**进行自增操作(如 `p++` 合法)。 ##### 问题: Q1:利用字符指针输出字符串,每行一个字符 这是一个经典的指针遍历字符串的应用。 ``` char *str = "Hello"; // 定义一个字符串 char *p = str; // 让指针 p 指向字符串的第一个字符 'H' // 循环条件:*p != '\0',即只要指针指向的不是字符串结束符 while (*p != '\0') { printf("%c\n", *p); // 输出当前指针指向的字符,并换行 p++; // 让指针向后移动一个位置,指向下一个字符 } ``` Q2:用指针遍历数组元素 指针可以通过算术运算来访问数组中的元素。 ``` int a[5] = {10, 20, 30, 40, 50}; int *p = a; // 指针 p 指向数组首地址 for (int i = 0; i < 5; i++) { // 写法 1:指针不移动,通过偏移量访问 printf("%d ", *(p + i)); // 写法 2:直接移动指针 (如果用这种写法,p 真的会变,要注意后续使用 p 的位置) // printf("%d ", *p++); } ``` #### 结构体与链表 ##### 结构体 (struct) 结构体是把不同类型的数据组合在一起的“大包裹”。 结构体数组的初始化 (P233) - **语法格式:** ``` struct 结构名 数组名[元素个数] = { {初值列表1}, {初值列表2}, ... }; ``` ``` struct Student { char name[20]; int age; }; // 定义并初始化一个包含 3 个学生的数组 struct Student stus[3] = { {"ZhangSan", 18}, {"LiSi", 19}, {"WangWu", 20} }; ``` ###### 问题: Q1:如何使用结构体变量? 有两种方式,取决于你手里拿的是“变量本身”还是“指针”。 1. **通过变量名访问 (点号** `.` **)** - 格式:`结构变量名.成员名` - 例子:`s.age = 18;` 2. **通过指针访问 (箭头** `->` **)** - 格式:`结构指针->成员名` - 例子: ``` struct Student s; struct Student *p = &s; // p 是指向 s 的指针 p->age = 18; // 等价于 (*p).age ``` Q2:如何查看结构体类型所占用的内存空间大小? - **方法:** 使用 `sizeof` 运算符。 - **格式:** `sizeof(struct 结构名)` 或 `sizeof(结构变量名)`。 - **注意(内存对齐):** - 结构体的大小通常**不等于**所有成员大小的简单相加。 - 编译器为了提高读写效率,会进行“内存对齐”(比如 `char` 后面可能会补空字节,以便让后面的 `int` 从 4 的倍数地址开始存放)。 - *考试技巧:* 如果问具体大小,通常只需算出总和即可(除非考题明确提及内存对齐/Padding)。 ##### 共用体 (union) (P251) 1. 核心概念 - **定义:** 使几个不同类型的变量共用**同一段**内存空间。 - **大小:** 共用体变量所占的内存长度,等于其**最长成员**的长度。 - **特点(喜新厌旧):** - 同一时刻只能存储其中一个成员的值。 - 对一个成员赋值,会**覆盖**掉其他成员的值(因为大家住同一个房间)。 ``` #include int main() { // 定义一个匿名共用体变量 s union { char c; // 占 1 字节 int i; // 占 4 字节(假设) } s; // s 的总大小是 4 字节(取最大的) s.c = 'A'; // 内存里写入了 'A' (65) s.i = 99; // 【关键】内存里写入了 99。由于 c 和 i 共用地址,这会覆盖掉刚才的 'A'。 printf("%d", s.i); // 此时内存里存的是 99,所以输出 99。 return 0; } ``` - **输出结果:** `99` - **解析:** 共用体的核心就是“共享”。`s.c` 和 `s.i` 的起始地址是一样的。当你执行 `s.i = 99` 时,原本 `s.c` 占用的那个字节也被 99 的数据覆盖了(或者成为了 99 的一部分)。最后读 `s.i`,当然就是刚刚存进去的 99。 ##### 链表(Brief Intro) (P246) 1. 节点的定义 链表节点 = **数据域** + **指针域**。 ``` struct Node { int data; // 数据域:存数据 struct Node *next; // 指针域:存下一个节点的地址 }; ``` 2. 常见考点 - **头指针 (head):** 指向链表第一个节点的指针。 - **结束标志:** 最后一个节点的 `next` 指针指向 `NULL`。 - **遍历:** `p = p->next;` (让指针 p 移动到下一个节点)。 ![[image-5f6c16ff.png]] #### 文件 ##### 文件类型 (P263) 数据文件主要分为两种: - **ASCII 文件 (文本文件):** 每个字符占一个字节,存储的是字符的 ASCII 码,可以直接阅读(如 .txt, .c)。 - **二进制文件 (Binary):** 将内存中的数据原样存储,不进行转换,不仅省空间而且读写快,但无法直接阅读(如 .exe, .jpg)。 ##### 文件的打开与关闭 (P266) ###### 打开文件 fopen - **格式:** `FILE *fp = fopen("文件名", "打开方式");` - **常见打开方式(扩展):** - `"r"` (read):**只读**。文件必须存在,否则报错。 - `"w"` (write):**只写**。文件不存在则新建,**存在则清空内容**(慎用!)。 - `"a"` (append):**追加**。文件不存在则新建,存在则在末尾追加。 - `"rb"`, `"wb"`, `"ab"`:对应的二进制文件操作。 - `"r+"`, `"w+"`:读写模式(较少考,了解即可)。 ###### 关闭文件 fclose - **格式:** `fclose(文件指针);` - **作用:** 释放缓冲区,断开连接,防止数据丢失。 ###### Q: 如何判断 fopen、fclose 函数执行成功? 1. **判断 fopen 成功:** 检查返回值是否为 `NULL`。 ``` FILE *fp = fopen("data.txt", "r"); if (fp == NULL) { printf("文件打开失败!\n"); return -1; // 或 exit(0); } ``` 2. **判断 fclose 成功:** 检查返回值是否为 `0`。 - 返回 `0`:成功关闭。 - 返回 `EOF` (-1):关闭失败。 ##### 文件读写操作详解 (P268 - P273) 不同函数用于读写不同类型的数据,考点在于**区分它们的适用场景**。 ###### Q: 各种读写函数的区别? | 函数对 | 读函数 (Input) | 写函数 (Output) | 处理单位 | 适用场景 | | --- | --- | --- | --- | --- | | **字符读写** | `fgetc(fp)` | `fputc(ch, fp)` | **单个字符** | 逐个字符处理文本文件 | | **字符串读写** | `fgets(str, n, fp)` | `fputs(str, fp)` | **字符串/行** | 按行读取文本文件 (注意 fgets 会读 n-1 个字符) | | **格式化读写** | `fscanf(fp, ...)` | `fprintf(fp, ...)` | **格式化数据** | 像 printf/scanf 一样读写含数字、文本的混合数据 | | **数据块读写** | `fread(...)` | `fwrite(...)` | **二进制块** | 读写结构体数组、图片等二进制数据 (效率最高) | - **P273 格式化读写补充:** - `fprintf(fp, "%d %s", age, name);` :把数据格式化后写入文件(变成文本)。 - `fscanf(fp, "%d %s", &age, name);` :从文件中按格式解析数据。 #### 编译预处理 (P281) ##### 1. 宏定义 - **格式:** `#define 标识符 字符串` - **例子:** `#define PI 3.14159` 或 `#define S(a,b) a*b` ###### Q: 预处理时对宏的处理过程? - **核心机制:** **单纯的文本替换**。 - **时机:** 发生在编译(Compile)之前。预处理器扫描源代码,把所有的宏名(如 `PI`)直接换成对应的字符串(如 `3.14159`)。 - **重要特性:** 预处理阶段**不做语法检查**,也**不做数值计算**。它只是傻傻地把字符搬过去。 - *易错点:* `#define N 2+3`,代码中 `N*N` 会被替换成 `2+3*2+3`,而不是 `25`。 ![[image-4311fef1.png]] ##### 2. 条件编译 ###### Q: #ifdef 和 #if 的区别? 1. `#ifdef 标识符` **(If Defined)** - **作用:** 判断某个宏**是否已经被定义过**(不管它的值是多少,哪怕是空的)。 - **逻辑:** 只要前面写过 `#define 标识符`,条件就成立。 - **场景:** 常用于防止头文件重复包含(Header Guard)。 2. `#if 表达式` - **作用:** 判断后面的**常量表达式**的值是否为真(非 0)。 - **逻辑:** 类似于 C 语言里的 `if` 语句,但只在编译前判断。如果表达式算出是 0,中间的代码直接被剪掉,不会被编译。 - **场景:** 用于版本控制,例如 `#if VERSION > 2`。 ![[image-a49d06ac.png]] ![[image-17fd952a.png]] ### 按题型(推测) #### **一、选择题(共20小题,每小题1分,共20分)** *(涵盖:基础、标识符、类型、运算符、流控、数组、指针、结构体、文件)* 1. **【基础】** C语言程序执行的入口是: A. 头文件 B. `main` 函数 C. 程序中的第一个函数 D. 预处理指令 2. **【标识符】** 下列合法的用户标识符是: A. `2ab` (数字开头) B. `int` (关键字) C. `_total` D. `a-b` (减号非法) 3. **【数据类型】** 若有 definition `unsigned short a = 0;`,执行 `a = a - 1;` 后,`a` 的值是: A. -1 B. 0 C. 65535 (类型最大值) D. 编译错误 4. **【常量与ASCII】** 已知字符 `'A'` 的 ASCII 码是 65,则执行 `printf("%d", 'C');` 的输出结果是: A. C B. 66 C. 67 D. c 5. **【字符串常量】** 字符串 `"Hello"` 在内存中占用的字节数是: A. 5 B. 6 (包含`\0`) C. 4 D. 8 6. **【宏定义】** 若有 `#define M 2+3`,则 `M * 2` 的值是: A. 10 B. 7 C. 8 (即 2+3\*2) D. 编译出错 7. **【运算符】** 表达式 `5 && 0 || 3` 的值是(注意优先级): A. 0 B. 1 C. 3 D. 假 8. **【自增陷阱】** 若 `int k=0;`,执行 `while(k++ < 3);` 后,k 的值是: A. 3 B. 4 (判断失败时已自增) C. 2 D. 5 9. **【逻辑短路】** 若 `int a=0, b=1;`,执行 `if(a++ && b++)` 后,a 和 b 的值分别为: A. 1, 1 (左假右不执行) B. 1, 2 C. 0, 1 D. 0, 2 10. **【输入输出】** 使用 `scanf` 输入 `double` 类型变量时,必须使用的格式符是: A. `%f` B. `%lf` C. `%d` D. `%ld` 11. **【数学函数】** 要计算浮点数的绝对值,应调用的函数是: A. `abs()` B. `fabs()` C. `sqrt()` D. `pow()` 12. **【if语句陷阱】** 代码 `if(a=5)` 表示的含义是: A. 判断 a 是否等于 5 B. 语法错误 C. 将 5 赋值给 a,条件永远为真 D. 将 5 赋值给 a,条件永远为假 13. **【Switch】** `switch(exp)` 语句中,表达式 `exp` 的类型**不能**是: A. `int` B. `char` C. `short` D. `float` 14. **【二维数组】** 下列二维数组定义合法的是(省略维度规则): A. `int a[2][]={1,2,3};` B. `int a[][3]={1,2,3};` (行可省,列不可省) C. `int a[][]={1,2,3};` D. `int a[][3];` 15. **【字符串比较】** `strcmp("abc", "abd")` 的返回值是: A. 0 B. 正数 C. 负数 ('c' < 'd') D. 1 16. **【指针】** 若有 `int a=10, *p=&a;`,则 `*p` 的值是: A. a 的地址 B. 10 C. p 的地址 D. 随机值 17. **【函数】** 关于形参和实参,下列说法正确的是: A. 形参不占内存 B. 实参和形参必须同名 C. 数据传递是单向的(实参传给形参) D. 函数内部修改形参,实参一定会变 18. **【共用体】** 若 `union {char c; int i;} u;`,已知 `int` 占4字节,则 `sizeof(u)` 为: A. 1 B. 4 (取最大成员长度) C. 5 D. 8 19. **【文件】** 若文件打开失败,`fopen` 函数的返回值是: A. -1 B. 0 C. `NULL` D. `EOF` 20. **【文件模式】** 使用模式 `"w"` 打开文件时,若文件已存在,则: A. 在文件尾追加 B. 报错 C. 保留原内容 D. 清空原内容 #### **二、填空题(共10空,每空1分,共10分)** *(涵盖:格式符、关键字、库函数、计算细节)* 1. C语言中,无符号整型 (`unsigned int`) 的格式化输出控制符是 %u。 2. `long long` 类型数据的输入输出格式控制符是 %lld。 3. 字符串 "China" 实际占用的内存字节数是 6。 4. 逻辑表达式 `!5` 的值是 0 (填0或1)。 5. 数学函数 `sqrt(x)` 要求参数 x 必须是 非负 数。 6. 数组下标从 0 开始。 7. 结构体类型的关键字是 struct ,共用体类型的关键字是 union 。 8. 在 `switch` 语句中,用于跳出 `switch` 结构的关键字是 break 。 9. 函数 `strlen("Hello\0World")` 的返回值是 5 (遇\0停止)。 #### **三、判断题(共10小题,每小题1分,共10分)** *(涵盖:基本概念易错点)* 1. ( √ ) 标识符 `_123` 是合法的 C 语言标识符。 2. ( × ) 在 C 语言中,`main` 函数必须写在所有自定义函数的最前面。 3. ( × ) 宏定义 `#define N 10;` (带分号) 是正确的,引用 `N` 时不会出错。 4. ( √ ) `do...while` 循环的循环体至少会执行一次。 5. ( × ) 数组名是一个地址常量,可以进行自增运算(如 `a++`)。 6. ( √ ) 表达式 `(a=3)` 的值是 3,且该表达式的结果为“真”。 7. ( × ) `unsigned short` 类型的变量可以正确存储负数(如 -5)。 8. ( √ ) 假如 `a < b`,则条件运算符 `a > b ? a : b` 的结果是 `b`。 9. ( × ) 在文件操作中,文本文件和二进制文件的存储形式是完全一样的。 10. ( √ ) 逻辑运算符 `&&` 具有短路特性,若左操作数为假,则右操作数不执行。 ## 程序阅读题(五题 差一道) ### 1、字符型变量 P33 #### 第一题:字符变量的本质(对应教材 P33 例 2.10) 这道题的核心考点是:**字符 'A' 和整数 65 在内存里是一回事。** ``` #include int main() { char c1, c2; c1 = 'A'; // 以字符形式赋值 c2 = 65; // 以ASCII码数值形式赋值(65对应'A') printf("%c, %c\n", c1, c2); // 考点1:都以字符形式输出 printf("%d, %d\n", c1, c2); // 考点2:都以整数形式输出 return 0; } ``` 问:上述程序的输出结果是什么? **答:** ``` A, A 65, 65 ``` - **赋值区别**:`c1` 赋的是字符 `'A'`,`c2` 赋的是整数 `65`。但在计算机看来,它俩存的都是二进制的 65。 - **输出区别**: - `%c`:把 65 当作字符看 -> 输出 `A`。 - `%d`:把 65 当作数字看 -> 输出 `65`。 #### 第二题:大小写转换运算(对应教材 P33 例 2.11) 这道题的核心考点是:**小写字母 = 大写字母 + 32**。 ``` #include int main() { char c1, c2; c1 = 'A'; // 初始化为大写 A c2 = 'B'; // 初始化为大写 B c1 = c1 + 32; // 考点:大写转小写运算 c2 = c2 + 32; // 66 + 32 = 98 printf("%c, %c\n", c1, c2); // 输出转换后的字符 printf("%d, %d\n", c1, c2); // 输出转换后的ASCII码数值 return 0; } ``` 问:上述程序的输出结果是什么? **答:** ``` a, b 97, 98 ``` - **运算逻辑**: - `c1` 原本是 `'A'(65)`,执行 `65 + 32 = 97`。97 对应的字符是 `'a'`。 - `c2` 原本是 `'B'(66)`,执行 `66 + 32 = 98`。98 对应的字符是 `'b'`。 - 大写转小写,加 32;小写转大写,减 32。 ### 2、sizeof() #### 第一题:基本数据类型的长度(对应教材 P34 例 2.15) ``` #include int main() { printf("char 类型占 %d 字节\n", sizeof(char)); printf("short int 类型占 %d 字节\n", sizeof(short int)); printf("int 类型占 %d 字节\n", sizeof(int)); printf("float 类型占 %d 字节\n", sizeof(float)); printf("double 类型占 %d 字节\n", sizeof(double)); return 0; } ``` ``` char 类型占 1 字节 short int 类型占 2 字节 int 类型占 4 字节 float 类型占 4 字节 double 类型占 8 字节 ``` #### 第二题:`sizeof` 陷阱与混合运算(对应教材 P34 例 2.16) 混合了**类型转换**、**运算符优先级**和**字符串长度**三个考点。 ``` #include int main() { int a, b; // 坑点1:表达式类型自动提升 a = sizeof(3 + 5.0); // 坑点2:优先级问题 b = sizeof 3 + 5.0; // 坑点3:字符串包含 \0 printf("%d, %d, %d\n", a, b, sizeof("China")); return 0; } ``` ``` 8, 9, 6 ``` - **分析** `a = sizeof(3 + 5.0);` - 括号里是 `3`(int) + `5.0`(double)。 - C 语言规则:**小类型遇到大类型,自动变大**。所以 `int` 变成 `double` 参与运算。 - 结果类型是 `double`。 - `sizeof(double)` = **8**。所以 `a = 8`。 - **分析** `b = sizeof 3 + 5.0;` **(最容易错!)** - 这里没有括号! - **优先级规则**:`sizeof` 的优先级比 `+` 加法**高**。 - 计算步骤: 1. 先算 `sizeof 3`:3 是 int,大小为 **4**。 2. 再算 `4 + 5.0`:变成 `4.0 + 5.0 = 9.0`。 3. 最后赋值给 `int b`:`9.0` 被强制截断成整数 **9**。 - 所以 `b = 9`。 - **分析** `sizeof("China")` - 字符串 "China" 有 5 个字母。 - **切记**:字符串末尾自动加 `\0`。 - 占地大小 = 5 + 1 = **6** 字节。 ### 3、函数递归 ``` void fun(int a) { if(!a) // 如果 a 是 0 printf("%d",a); // 打印 0 else fun(a-1); // 递归调用,参数减 1 printf("%d",a); // 【注意】这行代码在 else 外面,无论走哪个分支,最后都会执行! } int main() { fun(2); ... } ``` 执行流程拆解 (Stack Trace) 1. **第一层** `fun(2)`: - `a=2`。`!2` 为假,走 `else`。 - 调用 `fun(1)` ... **(暂停,等待子函数返回)** 2. **第二层** `fun(1)`: - `a=1`。`!1` 为假,走 `else`。 - 调用 `fun(0)` ... **(暂停,等待子函数返回)** 3. **第三层** `fun(0)`: - `a=0`。`!0` 为真,走 `if`。 - 执行 `printf("%d", 0)` -> **输出 0** - if 结束,继续向下。 - 执行 `printf("%d", 0)` -> **输出 0** - 函数结束,**返回**到上一层。 4. **回到第二层** `fun(1)`: - `fun(0)` 调用结束。 - 继续执行下面的 `printf("%d", 1)` -> **输出 1** - 函数结束,**返回**到上一层。 5. **回到第一层** `fun(2)`: - `fun(1)` 调用结束。 - 继续执行下面的 `printf("%d", 2)` -> **输出 2** - 函数结束。 最终输出结果 ``` 0012 ``` ### 4、宏定义 ``` #include #define LAB1 0 #define LAB2 2 #include void main() { int temp; #ifdef LAB1 temp=LAB1; #else temp=LAB2; #endif printf("%d",temp); } ``` ``` 0 ``` ### 5、可能是带有 `continue` 或 `break` 的循环,或者是逻辑短路测试。 ## 算法设计题(两题) ### 1、循环(二层) #### 第一题:编程计算3×3的矩阵的两条对角线上所有元素之和。 #### ![[image-6b1e8bb4.png]] ``` #include int main() { int a[3][3] = {9, 4, 7, 5, 6, 1, 8, 3, 2}, sum = 0, i, j; printf("数组:\n"); // 输出矩阵部分 for(i = 0; i < 3; i++) /* 输出数组 */ { for(j = 0; j < 3; j++) printf("%3d", a[i][j]); printf("\n"); /* 换行 */ } // 计算求和部分 for(i = 0; i < 3; i++) /* 求主副对角线元素之和 */ for(j = 0; j < 3; j++) // i==j 是主对角线,i+j==2 是副对角线(因为维度是3,最大下标是2) if(i == j || i + j == 2) /* 判断是否为主副对角线元素 */ sum = sum + a[i][j]; printf("对角线元素之和为:%d\n", sum); return 0; } ``` #### 第二题:编写程序,求3×4的矩阵的转置矩阵。 ![[image-f248d54b.png]] ``` #include int main() { // a 是 3行4列,b 是 4行3列 int a[3][4] = {2, 5, 7, 9, 6, 14, 20, 8, 15, 0, 12, 3}, b[4][3], i, j; printf("数组 a:\n"); for(i = 0; i < 3; i++) /* 输出矩阵 a */ { for(j = 0; j < 4; j++) printf("%-4d", a[i][j]); printf("\n"); } // 转置核心逻辑:将 a 的行 i 给 b 的列 i,a 的列 j 给 b 的行 j for(i = 0; i < 4; i++) /* 矩阵 a 转置成矩阵 b */ for(j = 0; j < 3; j++) b[i][j] = a[j][i]; printf("数组 b:\n"); for(i = 0; i < 4; i++) /* 输出矩阵 b */ { for(j = 0; j < 3; j++) printf("%-4d", b[i][j]); printf("\n"); } return 0; } ``` ### 2、字符数组指针 将26个英文字母以每行10个的形式输出: ![[image-74458c0d.png]] ``` #include int main() { char *str1, *str2; str1 = "abcdefghijklmnopqrstuvwxyz"; // 遍历字符 for(str2 = str1; str2 < str1 + 26; str2++) { printf("%c ", *str2); // 判断是否换行 // str2-str1 得到的是当前字符的下标(0-25) // +1 是为了变成第几个字符(1-26) if((str2 - str1 + 1) % 10 == 0) printf("\n"); } return 0; } ``` ### ## 程序设计题(两题) ### 1、画流程图(循环语句) 求两个数a和b的最大公约数、最小公倍数 流程图 P96,例题5.17最大公约数,最小公倍数流程图画出来 ``` /* 【例 5.17】求两个自然数的最大公约数和最小公倍数 */ #include int main() { int a, b, r, n, m; printf("请输入两个整数:"); scanf("%d%d", &a, &b); m = a; n = b; /* 保存原始数值,因为后面 a 和 b 会变 */ /* 核心算法:辗转相除法 */ do { r = a % b; /* 求余数 */ a = b; /* 把除数 b 变成下一轮的被除数 a */ b = r; /* 把余数 r 变成下一轮的除数 b */ } while (r != 0); /* 当余数不为0时,继续循环;余数为0,结束 */ printf("%d和%d的最大公约数是:%d\n", m, n, a); printf("最小公倍数是:%d\n", m * n / a); return 0; } ``` ![[image-31f8f99d.png]] ### 2、估计还有一道应该是分支语句 #### 题目一:寻找最大的数 ![[image-e83f60a2.png]] #### 题目二:有3个数 a, b, c,要求按大小顺序把它们输出 ![[image-0e8d25ad.png]] #### 题目三:判断一个数 n 能否同时被 3 和 5 整除 ![[image-e2352b0f.png]] ### 3、要么就是编程题 ``` 问题:编程打印数字金字塔。 0 123 23456 3456789 ``` ``` #include int main() { int height = 4; // 金字塔高度 int i, j; for (i = 0; i < height; i++) { // 打印每行前的空格,使数字居中 for (j = 0; j < height - i - 1; j++) { printf(" "); } // 打印数字,每行从 i 开始,长度为 2*i + 1 for (j = 0; j < 2 * i + 1; j++) { printf("%d", i + j); } // 换行 printf("\n"); } return 0; } ``` ``` 问题:求阶乘。 ``` ``` #include int main() { int n; unsigned long long fact = 1; scanf("%d", &n); if (n < 0) { printf("阶乘只适用于非负整数。\n"); return 1; } for (int i = 1; i <= n; i++) { fact *= i; } printf("%d 的阶乘是 %llu\n", n, fact); return 0; } ```