From 274c6985cf3c5f62731654136c96d90d8993e08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 19:23:35 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E9=87=8D=E6=9E=84=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=9Aslide/=20=E9=9B=86=E4=B8=AD=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=89=80=E6=9C=89=E5=B9=BB=E7=81=AF=E7=89=87=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - slides/ → slide/chapters/:幻灯片片段移至子目录 - main.tex → slide/main.tex:入口文件收归 slide/ - .latexmkrc 移至 slide/,输出到 slide/build/ - 根目录零 main.* 残留 - 更新所有脚本(bat/ps1/py)和文档(AGENTS.md/README.md)的路径引用 - 修复图片路径:images/ → ../images/(适配 main.tex 新位置) --- AGENTS.md | 9 +++--- README.md | 30 ++++++++++--------- note/.gitkeep | 0 scripts/build.bat | 7 +++-- scripts/build.ps1 | 7 +++-- scripts/clean.bat | 8 ++--- scripts/gen_note.py | 2 +- .latexmkrc => slide/.latexmkrc | 0 {slides => slide/chapters}/01-title.tex | 4 +-- {slides => slide/chapters}/02-outline.tex | 0 {slides => slide/chapters}/10-choice-1.tex | 0 {slides => slide/chapters}/11-choice-2.tex | 0 {slides => slide/chapters}/20-basic-blank.tex | 0 {slides => slide/chapters}/30-reading-1.tex | 0 {slides => slide/chapters}/31-reading-2.tex | 0 {slides => slide/chapters}/40-complete.tex | 0 {slides => slide/chapters}/50-programming.tex | 0 {slides => slide/chapters}/99-end.tex | 0 main.tex => slide/main.tex | 20 ++++++------- 19 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 note/.gitkeep rename .latexmkrc => slide/.latexmkrc (100%) rename {slides => slide/chapters}/01-title.tex (63%) rename {slides => slide/chapters}/02-outline.tex (100%) rename {slides => slide/chapters}/10-choice-1.tex (100%) rename {slides => slide/chapters}/11-choice-2.tex (100%) rename {slides => slide/chapters}/20-basic-blank.tex (100%) rename {slides => slide/chapters}/30-reading-1.tex (100%) rename {slides => slide/chapters}/31-reading-2.tex (100%) rename {slides => slide/chapters}/40-complete.tex (100%) rename {slides => slide/chapters}/50-programming.tex (100%) rename {slides => slide/chapters}/99-end.tex (100%) rename main.tex => slide/main.tex (92%) diff --git a/AGENTS.md b/AGENTS.md index 75ff66d..b9891c4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ LaTeX beamer 幻灯片项目(96页),ctexbeamer + metropolis 主题,xelat ## 编译命令 ```bash -latexmk -xelatex main.tex # 编译到 build/main.pdf +latexmk -xelatex slide/main.tex # 编译到 slide/build/main.pdf scripts\build.bat # 双击运行(带暂停) pwsh scripts\build.ps1 # PowerShell 编译 scripts\clean.bat # 清理 build/ @@ -19,10 +19,11 @@ python scripts\gen_note.py # 从 PDF 生成发言稿 speaker_notes.txt ## 项目结构 ``` -main.tex — 入口:文档类 / 主题 / 配色 / \lstset / \input 所有 slide -.latexmkrc — latexmk 编译配置 +slide/main.tex — 入口:文档类 / 主题 / 配色 / \lstset / \input 所有章节 +.latexmkrc — latexmk 编译配置(输出到 slide/build/) README.md — 人类可读项目说明(幻灯片索引、知识点映射) -slides/ — 10 个 .tex 片段(不完整文档,由 main.tex \input) +slide/chapters/ — 幻灯片 LaTeX 片段(不完整文档,由 slide/main.tex \input) +note/ — 讲稿 LaTeX 文件(待创建) scripts/ — build.bat / build.ps1 / clean.bat / gen_note.py materials/ — 原始试卷 PDF(gitignore) figures/ — 图片资源 diff --git a/README.md b/README.md index e2dd32a..ba176f3 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,14 @@ ``` . -├── main.tex ← 入口文件:文档类 / 主题 / 配色 / \lstset / \input 所有 slide -├── .latexmkrc ← latexmk 编译配置(xelatex,输出到 build/) -├── .gitignore ← 忽略构建产物(*.aux, *.log, build/, *.pdf, materials/) -├── AGENTS.md ← AI Agent 项目说明 +├── slide/ +│ ├── main.tex ← 入口:文档类 / 主题 / 配色 / \lstset / \input 所有章节 +│ ├── chapters/ ← 幻灯片片段文件(由 slide/main.tex \input 引入) +│ └── build/ ← 编译输出(PDF 等) +├── .latexmkrc ← latexmk 编译配置(xelatex,输出到 slide/build/) +├── .gitignore ← 忽略构建产物(*.aux, *.log, build/, *.pdf, materials/) +├── AGENTS.md ← AI Agent 项目说明 ├── README.md -├── slides/ ← 幻灯片片段文件(由 main.tex \input 引入) │ ├── 01-title.tex ← 标题页 │ ├── 02-outline.tex ← 目录 / 讲座大纲 │ ├── 10-choice-1.tex ← 选择题 第 1—10 题 @@ -41,7 +43,7 @@ ## 幻灯片内容索引 -### 选择题(一)— `slides/10-choice-1.tex` +### 选择题(一)— `slide/chapters/10-choice-1.tex` | 题号 | 知识点 | |------|--------| @@ -56,7 +58,7 @@ | 9 | 字符数组声明(拆分为题目 + 解析两帧) | | 10 | 二维数组下标越界 | -### 选择题(二)— `slides/11-choice-2.tex` +### 选择题(二)— `slide/chapters/11-choice-2.tex` | 题号 | 知识点 | |------|--------| @@ -71,7 +73,7 @@ | 19 | 字符串指针数组 — 类型判断 | | 20 | 字符串指针输出(拆分为题目 + 解析两帧) | -### 基本概念填空题 — `slides/20-basic-blank.tex` +### 基本概念填空题 — `slide/chapters/20-basic-blank.tex` | 空号 | 知识点 | |------|--------| @@ -81,7 +83,7 @@ | 8 | 结构体初始化 | | 9—10 | 逗号表达式 | -### 阅读程序填空题(一)— `slides/30-reading-1.tex` +### 阅读程序填空题(一)— `slide/chapters/30-reading-1.tex` | 题号 | 知识点 | |------|--------| @@ -91,7 +93,7 @@ | 4 | 字符处理 | | 5 | 递归函数(拆分为代码 + 解析两帧) | -### 阅读程序填空题(二)— `slides/31-reading-2.tex` +### 阅读程序填空题(二)— `slide/chapters/31-reading-2.tex` | 题号 | 知识点 | |------|--------| @@ -101,7 +103,7 @@ | 9 | 数组奇偶处理(拆分为代码 + 解析两帧) | | 10 | 异或交换(拆分为代码 + 解析两帧) | -### 程序完善题 — `slides/40-complete.tex` +### 程序完善题 — `slide/chapters/40-complete.tex` | 题号 | 知识点 | |------|--------| @@ -109,7 +111,7 @@ | 2 | 数组查找(拆分为代码 + 答案两帧) | | 3 | 统计非负数(拆分为代码 + 答案两帧) | -### 编程题 — `slides/50-programming.tex` +### 编程题 — `slide/chapters/50-programming.tex` | 内容 | 说明 | |------|------| @@ -131,7 +133,7 @@ ```bash # 方式一:latexmk(推荐) -latexmk -xelatex main.tex +latexmk -xelatex slide/main.tex # 方式二:双击批处理 scripts\build.bat @@ -140,7 +142,7 @@ scripts\build.bat pwsh scripts\build.ps1 ``` -编译成功后,PDF 输出至 `build/main.pdf`。 +编译成功后,PDF 输出至 `slide/build/main.pdf`。 ### 清理 diff --git a/note/.gitkeep b/note/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/build.bat b/scripts/build.bat index b884e8c..69c2c37 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -1,13 +1,14 @@ @echo off echo === C语言期末模拟卷讲评 - 编译 === echo. -echo 正在编译 main.tex (xelatex) ... -cd /d "%~dp0.." +echo 正在编译 slide\main.tex (xelatex) ... +cd /d "%~dp0..\slide" latexmk -xelatex -interaction=nonstopmode main.tex +cd /d "%~dp0.." if %errorlevel% equ 0 ( echo. echo === 编译成功 === - echo PDF 已生成: build\main.pdf + echo PDF 已生成: slide\build\main.pdf ) else ( echo. echo === 编译失败,请检查上方错误信息 === diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 4fd48db..ded073e 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -1,13 +1,14 @@ $ErrorActionPreference = "Stop" Write-Host "=== C语言期末模拟卷讲评 - 编译 ===" -ForegroundColor Cyan Write-Host "" -Write-Host "正在编译 main.tex (xelatex) ..." -ForegroundColor Yellow -Set-Location -LiteralPath "$PSScriptRoot\.." +Write-Host "正在编译 slide\main.tex (xelatex) ..." -ForegroundColor Yellow +Push-Location -LiteralPath "$PSScriptRoot\..\slide" latexmk -xelatex -interaction=nonstopmode main.tex +Pop-Location if ($LASTEXITCODE -eq 0) { Write-Host "" Write-Host "=== 编译成功 ===" -ForegroundColor Green - $pdf = Get-Item "build\main.pdf" + $pdf = Get-Item "slide\build\main.pdf" -ErrorAction SilentlyContinue; if (-not $pdf) { $pdf = Get-Item "..\slide\build\main.pdf" } Write-Host "PDF: $($pdf.FullName) ($([math]::Round($pdf.Length/1KB,1))KB, $($pdf.LastWriteTime))" -ForegroundColor Green } else { Write-Host "" diff --git a/scripts/clean.bat b/scripts/clean.bat index 04705f1..8cb6acc 100644 --- a/scripts/clean.bat +++ b/scripts/clean.bat @@ -1,11 +1,11 @@ @echo off echo === C语言期末模拟卷讲评 - 清理 === cd /d "%~dp0.." -if exist build ( - echo 正在删除 build\ 目录... - rmdir /s /q build +if exist slide\build ( + echo 正在删除 slide\build\ 目录... + rmdir /s /q slide\build echo 清理完成 ) else ( - echo build\ 目录不存在,无需清理 + echo slide\build\ 目录不存在,无需清理 ) pause diff --git a/scripts/gen_note.py b/scripts/gen_note.py index 49b27e8..38c36b9 100644 --- a/scripts/gen_note.py +++ b/scripts/gen_note.py @@ -5,7 +5,7 @@ import pdfplumber import os -pdf_path = os.path.join(os.path.dirname(__file__), '..', 'build', 'main.pdf') +pdf_path = os.path.join(os.path.dirname(__file__), '..', 'slide', 'build', 'main.pdf') if not os.path.exists(pdf_path): print(f"PDF 不存在: {pdf_path}") print("请先运行 build.bat 编译生成 PDF") diff --git a/.latexmkrc b/slide/.latexmkrc similarity index 100% rename from .latexmkrc rename to slide/.latexmkrc diff --git a/slides/01-title.tex b/slide/chapters/01-title.tex similarity index 63% rename from slides/01-title.tex rename to slide/chapters/01-title.tex index 09a812c..79f2b22 100644 --- a/slides/01-title.tex +++ b/slide/chapters/01-title.tex @@ -1,9 +1,9 @@ \begin{frame} \begin{tikzpicture}[overlay, remember picture] \node[anchor=north east, xshift=-0.4cm, yshift=-0.4cm] at (current page.north east) { - \includegraphics[height=1.1cm]{images/致远书院.png}% + \includegraphics[height=1.1cm]{../images/致远书院.png}% \hspace{0.35cm}% - \includegraphics[height=1.1cm]{images/ENI-name-logo.png} + \includegraphics[height=1.1cm]{../images/ENI-name-logo.png} }; \end{tikzpicture} \titlepage diff --git a/slides/02-outline.tex b/slide/chapters/02-outline.tex similarity index 100% rename from slides/02-outline.tex rename to slide/chapters/02-outline.tex diff --git a/slides/10-choice-1.tex b/slide/chapters/10-choice-1.tex similarity index 100% rename from slides/10-choice-1.tex rename to slide/chapters/10-choice-1.tex diff --git a/slides/11-choice-2.tex b/slide/chapters/11-choice-2.tex similarity index 100% rename from slides/11-choice-2.tex rename to slide/chapters/11-choice-2.tex diff --git a/slides/20-basic-blank.tex b/slide/chapters/20-basic-blank.tex similarity index 100% rename from slides/20-basic-blank.tex rename to slide/chapters/20-basic-blank.tex diff --git a/slides/30-reading-1.tex b/slide/chapters/30-reading-1.tex similarity index 100% rename from slides/30-reading-1.tex rename to slide/chapters/30-reading-1.tex diff --git a/slides/31-reading-2.tex b/slide/chapters/31-reading-2.tex similarity index 100% rename from slides/31-reading-2.tex rename to slide/chapters/31-reading-2.tex diff --git a/slides/40-complete.tex b/slide/chapters/40-complete.tex similarity index 100% rename from slides/40-complete.tex rename to slide/chapters/40-complete.tex diff --git a/slides/50-programming.tex b/slide/chapters/50-programming.tex similarity index 100% rename from slides/50-programming.tex rename to slide/chapters/50-programming.tex diff --git a/slides/99-end.tex b/slide/chapters/99-end.tex similarity index 100% rename from slides/99-end.tex rename to slide/chapters/99-end.tex diff --git a/main.tex b/slide/main.tex similarity index 92% rename from main.tex rename to slide/main.tex index ae3235e..4621385 100644 --- a/main.tex +++ b/slide/main.tex @@ -98,26 +98,26 @@ \begin{document} -\input{slides/01-title} -\input{slides/02-outline} +\input{chapters/01-title} +\input{chapters/02-outline} % ========== 选择题 ========== -\input{slides/10-choice-1} -\input{slides/11-choice-2} +\input{chapters/10-choice-1} +\input{chapters/11-choice-2} % ========== 基本概念填空题 ========== -\input{slides/20-basic-blank} +\input{chapters/20-basic-blank} % ========== 阅读程序填空题 ========== -\input{slides/30-reading-1} -\input{slides/31-reading-2} +\input{chapters/30-reading-1} +\input{chapters/31-reading-2} % ========== 程序完善题 ========== -\input{slides/40-complete} +\input{chapters/40-complete} % ========== 编程题 ========== -\input{slides/50-programming} +\input{chapters/50-programming} -\input{slides/99-end} +\input{chapters/99-end} \end{document} From 4c3d77f910be50bbd3ab22cc6426ae4af6b72bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 19:27:28 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=AF=B4=E6=98=8E?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=BB=A5=E5=8F=8D=E6=98=A0=E6=96=B0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 所有 main.tex → slide/main.tex - .latexmkrc 路径更新为 slide/.latexmkrc - build/ → slide/build/ - 编译命令更新为 cd slide && latexmk -xelatex main.tex - 移除遗留 build/ 目录引用 --- AGENTS.md | 14 +++++++------- README.md | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b9891c4..b4bfe75 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,22 +5,22 @@ LaTeX beamer 幻灯片项目(96页),ctexbeamer + metropolis 主题,xelat ## 编译命令 ```bash -latexmk -xelatex slide/main.tex # 编译到 slide/build/main.pdf +cd slide && latexmk -xelatex main.tex # 编译到 slide/build/main.pdf scripts\build.bat # 双击运行(带暂停) pwsh scripts\build.ps1 # PowerShell 编译 -scripts\clean.bat # 清理 build/ +scripts\clean.bat # 清理 slide/build/ python scripts\gen_note.py # 从 PDF 生成发言稿 speaker_notes.txt ``` - **必须 xelatex**,不可 pdflatex(中文支持) - `.latexmkrc` 配置 `$out_dir = 'build'`,`-interaction=nonstopmode` -- 构建产物(aux/log/out/toc/nav/snm/vrb 等)、`build/`、`*.pdf`、`materials/` 已加入 `.gitignore` +- 构建产物(aux/log/out/toc/nav/snm/vrb 等)、`build/`、`*.pdf`、`materials/`、`.idea/`、`.sisyphus/` 已加入 `.gitignore` ## 项目结构 ``` slide/main.tex — 入口:文档类 / 主题 / 配色 / \lstset / \input 所有章节 -.latexmkrc — latexmk 编译配置(输出到 slide/build/) +slide/.latexmkrc — latexmk 编译配置(输出到 build/,即 slide/build/) README.md — 人类可读项目说明(幻灯片索引、知识点映射) slide/chapters/ — 幻灯片 LaTeX 片段(不完整文档,由 slide/main.tex \input) note/ — 讲稿 LaTeX 文件(待创建) @@ -32,7 +32,7 @@ code/ — 示例 C 代码 ## 配色方案 -克莱因蓝 (International Klein Blue, RGB 0,47,167) 全蓝色系,定义在 `main.tex` 前言区: +克莱因蓝 (International Klein Blue, RGB 0,47,167) 全蓝色系,定义在 `slide/main.tex` 前言区: | 色名 | RGB | 用途 | |------|-----|------| @@ -42,7 +42,7 @@ code/ — 示例 C 代码 | accentlight | (222,234,254) | block 背景 | | bglight / bglighter | (244,247,254) / (250,251,255) | block body / code 背景 | -修改配色只需改 `main.tex` 中 `\definecolor` 和 `\setbeamercolor` 块,不要散落到各 slide 文件。 +修改配色只需改 `slide/main.tex` 中 `\definecolor` 和 `\setbeamercolor` 块,不要散落到各 slide 文件。 ## 幻灯片文件约定 @@ -84,7 +84,7 @@ code/ — 示例 C 代码 当解析内容过多导致底部溢出时,按优先级处理: -1. **全局** `main.tex` 已设 `basicstyle=\footnotesize\ttfamily` + itemize `itemsep=0.08em` +1. **全局** `slide/main.tex` 已设 `basicstyle=\footnotesize\ttfamily` + itemize `itemsep=0.08em` 2. **局部** 对溢出 block 添加 `\small` 或 `\footnotesize` 3. **拆分** 将一帧拆为「题目」+「解析」两帧,标题加 `--- 题目` / `--- 解析` 后缀(如 Q9、Q16、Q20) 4. **极限** 仅代码帧可将 lstlisting 局部设为 `\scriptsize\ttfamily` 或 `\tiny\ttfamily` diff --git a/README.md b/README.md index ba176f3..5d0b942 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ ├── slide/ │ ├── main.tex ← 入口:文档类 / 主题 / 配色 / \lstset / \input 所有章节 │ ├── chapters/ ← 幻灯片片段文件(由 slide/main.tex \input 引入) +│ ├── .latexmkrc ← latexmk 编译配置(xelatex,输出到 build/) │ └── build/ ← 编译输出(PDF 等) -├── .latexmkrc ← latexmk 编译配置(xelatex,输出到 slide/build/) ├── .gitignore ← 忽略构建产物(*.aux, *.log, build/, *.pdf, materials/) ├── AGENTS.md ← AI Agent 项目说明 ├── README.md @@ -33,12 +33,12 @@ ├── scripts/ ← 编译与辅助脚本 │ ├── build.bat ← Windows 批处理编译(带暂停) │ ├── build.ps1 ← PowerShell 编译 -│ ├── clean.bat ← 清理 build/ 目录 +│ ├── clean.bat ← 清理 slide/build/ 目录 │ └── gen_note.py ← 从 PDF 提取文本生成发言稿 ├── materials/ ← 原始试卷 PDF(不纳入版本控制) ├── figures/ ← 图片资源 ├── code/ ← 示例代码 -└── build/ ← 编译产物目录(gitignore) +└── note/ ← 讲稿 LaTeX(待创建) ``` ## 幻灯片内容索引 @@ -133,7 +133,7 @@ ```bash # 方式一:latexmk(推荐) -latexmk -xelatex slide/main.tex +cd slide && latexmk -xelatex main.tex # 方式二:双击批处理 scripts\build.bat @@ -147,7 +147,7 @@ pwsh scripts\build.ps1 ### 清理 ```bash -# 删除 build/ 目录 +# 删除 slide/build/ 目录 scripts\clean.bat ``` From e9096e54c6756321f087be609d1ee4eb711e3bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 19:45:12 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=AE=B2=E7=A8=BF?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=EF=BC=9Anote/=20=E7=9B=AE=E5=BD=95=E5=8F=8A?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=9E=84=E5=BB=BA=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - note/main.tex + .latexmkrc:讲稿入口及编译配置 - note/chapters/:8个讲稿章节,与幻灯片内容同步 - 00-opening:讲座开场、模拟考试概况 - 10-choice-1:选择题 Q1-10 讲稿 - 11-choice-2:选择题 Q11-20 讲稿 - 20-basic-blank:基本概念填空题讲稿 - 30-reading:阅读程序 Q1-5 讲稿 - 31-reading-2:阅读程序 Q6-10 讲稿 - 40-complete:程序完善题 Q1-3 讲稿 - 50-programming:编程题电梯调度系统讲稿 - scripts/build-all.bat:一键构建 PPT + 讲稿 - scripts/build-all.ps1:PowerShell 完整构建 - scripts/clean-all.bat:清理所有构建产物 - 更新 AGENTS.md / README.md:构建命令、项目结构 --- AGENTS.md | 11 +- README.md | 33 +- note/.latexmkrc | 5 + note/chapters/00-opening.tex | 77 +++++ note/chapters/10-choice-1.tex | 169 +++++++++++ note/chapters/11-choice-2.tex | 291 ++++++++++++++++++ note/chapters/20-basic-blank.tex | 157 ++++++++++ note/chapters/30-reading.tex | 281 +++++++++++++++++ note/chapters/31-reading-2.tex | 324 ++++++++++++++++++++ note/chapters/40-complete.tex | 341 +++++++++++++++++++++ note/chapters/50-programming.tex | 507 +++++++++++++++++++++++++++++++ note/main.tex | 80 +++++ scripts/build-all.bat | 39 +++ scripts/build-all.ps1 | 46 +++ scripts/clean-all.bat | 16 + 15 files changed, 2361 insertions(+), 16 deletions(-) create mode 100644 note/.latexmkrc create mode 100644 note/chapters/00-opening.tex create mode 100644 note/chapters/10-choice-1.tex create mode 100644 note/chapters/11-choice-2.tex create mode 100644 note/chapters/20-basic-blank.tex create mode 100644 note/chapters/30-reading.tex create mode 100644 note/chapters/31-reading-2.tex create mode 100644 note/chapters/40-complete.tex create mode 100644 note/chapters/50-programming.tex create mode 100644 note/main.tex create mode 100644 scripts/build-all.bat create mode 100644 scripts/build-all.ps1 create mode 100644 scripts/clean-all.bat diff --git a/AGENTS.md b/AGENTS.md index b4bfe75..df21131 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,9 +6,9 @@ LaTeX beamer 幻灯片项目(96页),ctexbeamer + metropolis 主题,xelat ```bash cd slide && latexmk -xelatex main.tex # 编译到 slide/build/main.pdf -scripts\build.bat # 双击运行(带暂停) -pwsh scripts\build.ps1 # PowerShell 编译 -scripts\clean.bat # 清理 slide/build/ +cd note && latexmk -xelatex main.tex # 编译到 note/build/main.pdf +scripts\build-all.bat # 一键构建 PPT + 讲稿 +scripts\clean-all.bat # 清理所有构建产物 python scripts\gen_note.py # 从 PDF 生成发言稿 speaker_notes.txt ``` @@ -23,8 +23,9 @@ slide/main.tex — 入口:文档类 / 主题 / 配色 / \lstset / \in slide/.latexmkrc — latexmk 编译配置(输出到 build/,即 slide/build/) README.md — 人类可读项目说明(幻灯片索引、知识点映射) slide/chapters/ — 幻灯片 LaTeX 片段(不完整文档,由 slide/main.tex \input) -note/ — 讲稿 LaTeX 文件(待创建) -scripts/ — build.bat / build.ps1 / clean.bat / gen_note.py +note/ — 讲稿 LaTeX 文件 (note/main.tex + note/chapters/) +note/.latexmkrc — latexmk 编译配置(输出到 build/,即 note/build/) +scripts/ — build-all.bat / build.ps1 / clean-all.bat / gen_note.py materials/ — 原始试卷 PDF(gitignore) figures/ — 图片资源 code/ — 示例 C 代码 diff --git a/README.md b/README.md index 5d0b942..09e0f79 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,19 @@ │ ├── 50-programming.tex ← 编程题:电梯调度系统 │ └── 99-end.tex ← 结束页 ├── scripts/ ← 编译与辅助脚本 -│ ├── build.bat ← Windows 批处理编译(带暂停) -│ ├── build.ps1 ← PowerShell 编译 -│ ├── clean.bat ← 清理 slide/build/ 目录 +│ ├── build.bat ← Windows 批处理编译幻灯片 +│ ├── build.ps1 ← PowerShell 编译幻灯片 +│ ├── build-all.bat ← 一键构建 PPT + 讲稿 +│ ├── clean.bat ← 清理幻灯片构建产物 +│ ├── clean-all.bat ← 清理所有构建产物 │ └── gen_note.py ← 从 PDF 提取文本生成发言稿 ├── materials/ ← 原始试卷 PDF(不纳入版本控制) ├── figures/ ← 图片资源 ├── code/ ← 示例代码 -└── note/ ← 讲稿 LaTeX(待创建) +├── note/ ← 讲稿 LaTeX 文件 +│ ├── main.tex ← 讲稿入口 +│ ├── .latexmkrc ← 讲稿编译配置 +│ └── chapters/ ← 讲稿章节文件 ``` ## 幻灯片内容索引 @@ -132,22 +137,28 @@ ### 快速编译 ```bash -# 方式一:latexmk(推荐) +# 方式一:latexmk(推荐)— 仅幻灯片 cd slide && latexmk -xelatex main.tex -# 方式二:双击批处理 -scripts\build.bat +# 方式二:完整构建 — 幻灯片 + 讲稿 +scripts\build-all.bat -# 方式三:PowerShell -pwsh scripts\build.ps1 +# 方式三:仅幻灯片 +scripts\build.bat # 批处理 +pwsh scripts\build.ps1 # PowerShell ``` -编译成功后,PDF 输出至 `slide/build/main.pdf`。 +编译成功后: +- 幻灯片 PDF 输出至 `slide/build/main.pdf` +- 讲稿 PDF 输出至 `note/build/main.pdf` ### 清理 ```bash -# 删除 slide/build/ 目录 +# 清理所有构建产物 +scripts\clean-all.bat + +# 仅清理幻灯片 scripts\clean.bat ``` diff --git a/note/.latexmkrc b/note/.latexmkrc new file mode 100644 index 0000000..b02af9d --- /dev/null +++ b/note/.latexmkrc @@ -0,0 +1,5 @@ +$xelatex = 'xelatex -synctex=1 -interaction=nonstopmode %O %S'; +$bibtex = ''; +$makeindex = ''; +$pdf_mode = 1; +$out_dir = 'build'; diff --git a/note/chapters/00-opening.tex b/note/chapters/00-opening.tex new file mode 100644 index 0000000..17eefc8 --- /dev/null +++ b/note/chapters/00-opening.tex @@ -0,0 +1,77 @@ +% ============================================ +% 讲座开场 +% ============================================ +\section{讲座开场} + +\subsection{自我介绍与讲座目标} + +各位同学好,我是你们的朋辈学业导师。今天的讲座内容是 \textbf{C语言期末模拟试卷的讲评}。 + +本次讲座的目标: +\begin{itemize} + \item 逐题讲解模拟试卷,帮助大家 \textbf{查漏补缺} + \item 重点分析 \textbf{易错知识点} 和 \textbf{常见陷阱} + \item 提炼 \textbf{解题思路与方法},而非仅仅对答案 +\end{itemize} + +\textbf{教学提示:} 此处可简短介绍自己,拉近与学生的距离。强调本次讲解以「模拟卷」为线索,但目标是覆盖期末考试的核心考点。 + +\subsection{模拟考试分值分布} + +本次模拟试卷满分 100 分,题型分布如下: + +\begin{table}[h] + \centering + \begin{tabular}{@{}lll@{}} + \toprule + \textbf{题型} & \textbf{题量} & \textbf{分值} \\ + \midrule + 选择题 & 20题(每题2分) & 40分 \\ + 基本概念填空题 & 10空(每空2分) & 20分 \\ + 阅读程序填空题 & 10空(每空2分) & 20分 \\ + 程序完善题 & 5空(每空2分) & 10分 \\ + 编程题 & 1题 & 10分 \\ + \bottomrule + \end{tabular} +\end{table} + +从分值来看,选择题占 40 分,是拿分的大头;填空题和阅读题各占 20 分,考验基本功;编程题 10 分,区分度最高。 + +\subsection{学生常见薄弱环节} + +根据往年考试和本次模拟的情况,以下几个知识点学生 \textbf{丢分最多}: + +\begin{enumerate} + \item \textbf{指针} —— 指针与数组的关系、指针作为函数参数、函数指针声明。很多同学「听得懂但做不对」。 + \item \textbf{递归} —— 递归调用过程的理解、递归与循环的转换。建议画调用栈图辅助理解。 + \item \textbf{宏定义展开} —— 宏是纯文本替换,不是函数调用!特别注意括号问题(如 \texttt{\#define SQR(x) x*x} 的陷阱)。 + \item \textbf{二维数组} —— 初始化方式、存储顺序(行优先)、下标范围。 + \item \textbf{逗号表达式} —— 从左到右依次求值,取最右值为结果。 +\end{enumerate} + +\textbf{教学提示:} 此处可追问学生:「你们觉得哪些题最难?」根据学生反馈适当调整后续讲解的详略。 + +\subsection{讲座安排} + +本次讲评按试卷顺序进行,分为以下几个板块: + +\begin{enumerate} + \item 选择题 Q1--Q10(标识符、优先级、字面量、ASCII、逻辑运算、break/continue、printf、数组声明与越界) + \item 选择题 Q11--Q20(函数、宏、指针、联合体、结构体、循环、static、字符串) + \item 基本概念填空题(源文件后缀、头文件、三种结构、取地址、复合赋值、函数声明、二维数组初始化、逗号表达式) + \item 阅读程序填空题(循环控制、指针参数、数组反转、字符处理、递归) + \item 程序完善题(矩阵运算、素数判断) + \item 编程题(电梯调度系统) +\end{enumerate} + +讲解过程中,对每道题我会先点明 \textbf{考查的知识点},再讲 \textbf{解题思路},最后提醒 \textbf{常见错误}。欢迎大家随时打断提问。 + +\subsection{参考建议} + +\begin{itemize} + \item 听讲时重点记 \textbf{「我当时为什么做错了」},而不是抄答案 + \item 讲座结束后,建议把错题 \textbf{重新做一遍},确保真正理解 + \item 编程题一定要 \textbf{上机练习},光看不练是学不会的 +\end{itemize} + +好,下面我们开始讲评。先从选择题 Q1--Q10 开始。 diff --git a/note/chapters/10-choice-1.tex b/note/chapters/10-choice-1.tex new file mode 100644 index 0000000..7db6f1f --- /dev/null +++ b/note/chapters/10-choice-1.tex @@ -0,0 +1,169 @@ +% ============================================ +% 选择题(一) Q1--Q10 +% ============================================ +\section{选择题(一):Q1--Q10} + +\subsection{Q1:合法标识符} + +\begin{itemize} + \item \textbf{考查知识点:} C语言标识符的命名规则。 + \item \textbf{关键结论:} 标识符由字母、数字、下划线组成,且 \textbf{不能以数字开头},\textbf{不能是关键字}。库函数名(如 \texttt{scanf})不是关键字,可以作为标识符使用。 + \item \textbf{逐项分析:} + \begin{itemize} + \item A (\texttt{26autumn}):数字开头 → 非法 + \item B (\texttt{scanf}):库函数名,但 \textbf{不是关键字} → 合法 ✓ + \item C (\texttt{Cour@e}):含特殊字符 \texttt{@} → 非法 + \item D (\texttt{while}):C语言 \textbf{关键字} → 非法 + \end{itemize} + \item {\color{highlight}\textbf{答案:B}} + \item \textbf{常见错误:} 很多同学认为 \texttt{scanf} 是关键字(因为天天用),但它实际是 \texttt{stdio.h} 中声明的库函数。C语言关键字只有 32 个(C89)。 + \item \textbf{教学提示:} 此处可追问:「\texttt{main} 是不是关键字?」(答案:不是,它是约定的主函数名) +\end{itemize} + +\subsection{Q2:运算符优先级} + +\begin{itemize} + \item \textbf{考查知识点:} 四类运算符的优先级排序。 + \item \textbf{关键结论:} 优先级从高到低: + \[ + \texttt{+(算术)} > \texttt{\&\&(逻辑与)} > \texttt{||(逻辑或)} > \texttt{=(赋值)} + \] + \item {\color{highlight}\textbf{答案:B(+)}} + \item \textbf{常见错误:} 混淆 \texttt{\&\&} 和 \texttt{||} 的优先级(\texttt{\&\&} 高于 \texttt{||},类似 \texttt{*} 高于 \texttt{+})。赋值运算符 \texttt{=} 的优先级最低,这几乎是所有语言的通则。 + \item \textbf{教学提示:} 建议学生记住优先级的口诀:「单算关位逻条赋」—— 单目 > 算术 > 关系 > 位运算 > 逻辑 > 条件 > 赋值。 +\end{itemize} + +\subsection{Q3:合法字面量} + +\begin{itemize} + \item \textbf{考查知识点:} 字符串常量、八进制/十六进制转义字符、浮点常量的合法性。 + \item \textbf{关键结论:} 八进制转义 \texttt{\textbackslash 0} 后面只能跟 0--7 的数字,\texttt{9} 不是合法八进制数字 → \texttt{'\textbackslash 091'} 非法。 + \item {\color{highlight}\textbf{答案:B}} + \item \textbf{常见错误:} + \begin{itemize} + \item 误以为 \texttt{"3e-3.5"} 不合法(字符串常量 \textbf{不检查} 内容语义) + \item 八进制数字范围记忆不清(0--7,非 0--9) + \item 科学计数法 \texttt{1e-3} 等价于 $1 \times 10^{-3} = 0.001$,是合法的 + \end{itemize} + \item \textbf{教学提示:} 此处可追问:「\texttt{'\textbackslash 087'} 合法吗?」(不合法,8 不是八进制数字) +\end{itemize} + +\subsection{Q4:ASCII码计算} + +\begin{itemize} + \item \textbf{考查知识点:} ASCII 码的连续性(大写字母 A--Z 依次递增)+ 十六进制加法。 + \item \textbf{关键结论:} 大写字母连续排列,K 比 H 多 3。$\text{0x}48 + 3 = \text{0x}4\text{B}$。两种算法:① 转十进制:$72 + 3 = 75 = 4\text{B}_{16}$;② 直接十六进制:$48_{16} + 3 = 4\text{B}_{16}$。 + \item {\color{highlight}\textbf{答案:D(4B)}} + \item \textbf{常见错误:} 十六进制加法不熟练,$48 + 3$ 算成 $51$ 或 $45$。「B」在十六进制中代表 11,$8+3=11$ 产生进位,十位从 4 进到 4、个位变 B → 4B。 + \item \textbf{教学提示:} 可以让学生直接背:'A' = 65 (0x41), 'a' = 97 (0x61),大小写差 32 (0x20)。 +\end{itemize} + +\subsection{Q5:\texttt{while(!(E))} 等价条件} + +\begin{itemize} + \item \textbf{考查知识点:} 逻辑非 \texttt{!} 的语义,条件的真/假判断。 + \item \textbf{关键结论:} + \[ + !(E) \text{ 为真 } \Leftrightarrow E \text{ 为假 } \Leftrightarrow E \texttt{ == } 0 + \] + \item {\color{highlight}\textbf{答案:D(E == 0)}} + \item \textbf{常见错误:} 把 \texttt{!(E)} 误解为「E 不等于 0」,恰好相反。可以用反证法想:如果 \texttt{!(E)} 等价于 \texttt{E != 0},那 \texttt{while(0)} 和 \texttt{while(!0)} 应该一个都不执行(矛盾)。 + \item \textbf{教学提示:} 此处可让学生当场验证:C语言中所有非零值视为「真」,零视为「假」。\texttt{!0 = 1},\texttt{!5 = 0}。 +\end{itemize} + +\subsection{Q6:break 与 continue} + +\begin{itemize} + \item \textbf{考查知识点:} break 和 continue 的 \textbf{使用范围} 和 \textbf{行为差异}。 + \item \textbf{关键结论:} + \begin{itemize} + \item \texttt{break}:只能用于 \textbf{循环体} 和 \textbf{switch 语句}。作用是退出当前循环/switch。 + \item \texttt{continue}:只能用于 \textbf{循环体}。作用是跳过本轮剩余语句,进入下一轮迭代。 + \end{itemize} + \item {\color{highlight}\textbf{答案:A}} + \item \textbf{常见错误:} + \begin{itemize} + \item 认为 break 可以在 if 中直接使用(不行,if 不是循环也不是 switch) + \item 认为 continue 终止整个循环(它只跳过本轮) + \item 认为多层循环只能用 goto 退出(可以用 break 配合标志变量逐层退出) + \end{itemize} + \item \textbf{教学提示:} 可在黑板上画循环流程图,标注 break 和 continue 分别跳转到哪里。 +\end{itemize} + +\subsection{Q7:逻辑表达式求值} + +\begin{itemize} + \item \textbf{考查知识点:} 运算符优先级(算术 > 关系 > \texttt{\&\&} > \texttt{||})与逻辑表达式的逐步求值。 + \item \textbf{逐步推导:} + \begin{enumerate} + \item 先算算术:\texttt{3 - 1 = 2} + \item 再算关系:\texttt{3 < 2} → 0(假),\texttt{4 > 2} → 1(真) + \item 非零值判真:\texttt{-1} ≠ 0 → 1(真) + \item 逻辑与:\texttt{1 \&\& 1 = 1} + \item 逻辑或:\texttt{0 || 1 = 1} + \end{enumerate} + \item {\color{highlight}\textbf{答案:A(1)}} + \item \textbf{常见错误:} + \begin{itemize} + \item 误以为结果会是 3 或 2(逻辑表达式的值只能是 \textbf{0 或 1},不可能是其他数字) + \item 把 \texttt{-1} 当成 0(C 语言中只有 0 是假,负数也是真!) + \end{itemize} + \item \textbf{教学提示:} 此处可以补充「短路求值」概念:\texttt{0 \&\& anything} 不再计算右边,直接得 0。 +\end{itemize} + +\subsection{Q8:printf 格式化输出} + +\begin{itemize} + \item \textbf{考查知识点:} \texttt{\%c}(字符)与 \texttt{\%d}(十进制整数)的区别。 + \item \textbf{关键结论:} \texttt{\%d} 输出的是字符的 \textbf{ASCII 码整数值},不是字符本身。'A' 的 ASCII 码是 65,'D' 的 ASCII 码是 68。 + \item \textbf{程序输出:} +\begin{lstlisting}[language=C] +char c1 = 'A', c2 = 'D'; +printf("%c, %d\n", c1, c2); // 输出: A, 68 +\end{lstlisting} + \item {\color{highlight}\textbf{答案:C(A, 68)}} + \item \textbf{常见错误:} 误以为 \texttt{\%d} 输出 'D' → 选 D 是常见错误。printf 格式说明符决定了 \textbf{输出形式},而非变量内容。 + \item \textbf{教学提示:} 此处可追问:「如果把 \texttt{\%c,\%d} 改成 \texttt{\%d,\%c} 会输出什么?」(输出:65, D) +\end{itemize} + +\subsection{Q9:字符数组声明} + +\begin{itemize} + \item \textbf{考查知识点:} 一维/二维字符数组的声明与初始化语法规则。 + \item \textbf{关键结论:} + \begin{itemize} + \item A 错:二维数组只能省略 \textbf{第一维}(行),不能省略第二维(列) + \item B 错:\texttt{"ABCDE"} 含末尾 \texttt{\textbackslash 0},共需 6 字节,\texttt{s2[5]} 不够 + \item C 错:两个维度都省略,编译器无法分配空间 + \item D 正确:用初始化列表逐个字符赋值,一维数组长度可省略 + \end{itemize} + \item {\color{highlight}\textbf{答案:D}} + \item \textbf{常见错误:} 忘记字符串末尾自动加 \texttt{\textbackslash 0},认为 \texttt{"ABCDE"} 只需要 5 字节。 + \item \textbf{教学提示:} 此处可强调:「用双引号初始化字符数组时,长度至少要 = 可见字符数 + 1」。想要不带 \texttt{\textbackslash 0} 就用初始化列表逐个字符赋值。 +\end{itemize} + +\subsection{Q10:二维数组下标越界} + +\begin{itemize} + \item \textbf{考查知识点:} 二维数组的下标范围。 + \item \textbf{关键结论:} \texttt{int a[3][4]} → 行下标 0$\sim$2,列下标 0$\sim$3。 + \item \textbf{逐项检查:} + \begin{itemize} + \item A (\texttt{a[1][3]}):行 1 ≤ 2 ✓,列 3 ≤ 3 ✓ → 合法 + \item B (\texttt{a[0][4]}):行 0 ≤ 2 ✓,列 4 > 3 ✗ → \textbf{越界!} + \item C (\texttt{a[0][2*1]}) = \texttt{a[0][2]}:列 2 ≤ 3 ✓ → 合法 + \item D (\texttt{a[4-2][0]}) = \texttt{a[2][0]}:行 2 ≤ 2 ✓,列 0 ≤ 3 ✓ → 合法 + \end{itemize} + \item {\color{highlight}\textbf{答案:B}} + \item \textbf{常见错误:} 把定义中的「4」误解为最大下标(实际是元素个数,最大下标 = 个数 $- 1$)。 + \item \textbf{教学提示:} 此处可追问:「C语言中数组越界会怎样?」(答案:不会报编译错误,可能导致运行时崩溃或数据被意外修改——这是 C 语言最危险的特性之一) +\end{itemize} + +% ========== 小结与过渡 ========== +\subsection{小结} + +Q1--Q10 覆盖了 C 语言的基础语法:标识符、运算符、字面量、ASCII、逻辑运算、流程控制、格式化输出和数组。这些知识点在期末考中几乎 \textbf{必考},请务必熟练掌握。 + +大家注意:Q2(优先级)、Q7(逻辑表达式)和 Q9(字符数组声明)是 \textbf{高频丢分题},建议课后重点回顾。 + +\textbf{教学提示:} 此处请稍作停顿,询问学生是否有疑问,再继续下一节。 diff --git a/note/chapters/11-choice-2.tex b/note/chapters/11-choice-2.tex new file mode 100644 index 0000000..b8559fa --- /dev/null +++ b/note/chapters/11-choice-2.tex @@ -0,0 +1,291 @@ +% ============================================ +% 选择题 Q11-20 讲稿 +% ============================================ +\section{选择题(二)} + +% ------------------------------------------------------- +\subsection{第11题:函数形参} +\textbf{概念概要:} 函数形参的语法规则——形参的类型、void 函数、形参与全局变量重名。 + +\textbf{核心讲解要点:} +\begin{itemize} + \item A 正确:形参可以是任何类型——整型、指针、结构体、数组(退化为指针)等都合法。让学生回忆见过的各种函数声明。 + \item B 错误:\texttt{void} 函数完全可以有形参,例如 \texttt{void printf\_stars(int n);}。\texttt{void} 只是\"不返回值\",与形参无关。 + \item C 错误:形参名会\"屏蔽\"同名的全局变量。这是 C 语言的\textbf{名字遮蔽}机制——局部作用域优先。可以举例:在函数体内你无法访问被屏蔽的全局变量。 + \item D 错误:无形参函数示例——\texttt{int getch(void);}、\texttt{int rand(void);}。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 学生常以为 \texttt{void func()} 就是\"不能有参数\",实际上 \texttt{void} 只修饰返回类型。 + \item 混淆\"形参名遮蔽全局变量\"和\"不能同名\"——语法上允许同名,结果是屏蔽而非冲突。 +\end{itemize} + +\textbf{讲师提示:} \textit{此题为送分题,快速过。重点强调\"形参能屏蔽全局变量\"这个知识点,后面的阅读程序可能会遇到。} + +{\color{highlight}答案:A} + +% ------------------------------------------------------- +\subsection{第12题:宏定义展开陷阱} + +\textbf{概念概要:} 宏是\underline{纯文本替换},不进行任何计算。这是宏与函数最本质的区别。 + +\smallskip +\textbf{讲师提示:} \textit{这是整个选择题中最重要的坑!务必让学生亲手在纸上展开一遍。} + +\textbf{展开过程 — 带学生逐步完成:} + +\noindent 宏定义:\texttt{\#define F(r)\quad r+r} + +\noindent 表达式:\texttt{F(3) * F(3)} + +\noindent \textbf{第1步}:将实参 3 替换形参 r,得到 \texttt{3+3 * 3+3} + +\noindent \textbf{第2步}:\underline{此时才进行运算符优先级分析}——乘法优先于加法 + +\noindent \textbf{第3步}:$3 + (3 \times 3) + 3 = 3 + 9 + 3 = \mathbf{15}$ + +\bigskip +\textbf{为什么不是 36?} 学生直觉往往会\"先算 \texttt{F(3)} 得 6,再 $6 \times 6 = 36$\"。但宏不先计算——它只做文本粘贴。要想得到 36,宏定义必须加括号: + +\begin{lstlisting}[language=C] +#define F(r) ((r)+(r)) // 安全的宏定义 +\end{lstlisting} + +\noindent 此时展开为 \texttt{((3)+(3)) * ((3)+(3)) = 6 * 6 = 36}。 + +\textbf{常见错误:} +\begin{itemize} + \item 把宏当函数用,以为会\"先求值再替换\"——这是最根本的错误。 + \item 忘记宏的副作用:\texttt{F(i++)} 展开为 \texttt{i++ + i++},i 自增两次,行为未定义! + \item 不加括号导致的优先级问题不仅限于算术,逻辑运算符也一样(如 \texttt{\#define AND(a,b) a\&\&b} 写成 \texttt{AND(x, y)==1} 的歧义)。 +\end{itemize} + +{\color{highlight}答案:C(15)} + +% ------------------------------------------------------- +\subsection{第13题:函数的定义与调用嵌套} + +\textbf{概念概要:} C 语言函数的定义与调用的嵌套规则。 + +\textbf{核心讲解要点:} +\begin{itemize} + \item \textbf{定义不可嵌套}:C 语言中所有函数定义都是\"平等\"的,不允许在一个函数体内定义另一个函数。这与 Pascal、Python 等语言不同。C 没有\"内部函数\"的概念。 + \item \textbf{调用可以嵌套}:函数 A 调用函数 B,B 调用函数 C——这是标准的嵌套调用。递归更是一种特殊的嵌套:函数调用自身。 + \item 标准 C(C89/C99/C11)不支持嵌套函数定义。GCC 有扩展支持但\underline{不可移植},考试中视为非法。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 有 Python 基础的学生可能习惯在函数内定义辅助函数,会错误认为 C 也支持。 + \item 混淆\"嵌套调用\"与\"递归调用\"——递归是调用的特例,\"嵌套调用\"更宽泛。 +\end{itemize} + +{\color{highlight}答案:B(定义不可嵌套,调用可嵌套)} + +% ------------------------------------------------------- +\subsection{第14题:指针下标运算} + +\textbf{概念概要:} 指针的下标运算本质是指针算术——\texttt{p[i] $\equiv$ *(p + i)},\texttt{p[-3] $\equiv$ *(p - 3)}。 + +\textbf{核心讲解要点:} +\begin{itemize} + \item 给定 \texttt{int *p = \&a[5];},即 p 指向数组元素 a[5](值为 6)。 + \item \texttt{p[-3]} 等价于 \texttt{*(p - 3)}:从 a[5] 向低地址方向移动 3 个 int 元素,到达 a[2]。 + \item a[2] 的值为 \textbf{3}。 + \item \textbf{关键理解:} 数组下标\underline{本质就是指针偏移}。a[i] 不过是 *(a+i) 的语法糖。因此下标可以为负,只要能合法访问即可。 +\end{itemize} + +\noindent 图示(帮助学生可视化): + +\begin{center} +\texttt{a: [1] [2] [3] [4] [5] [6] [7] [8] [?] [?]}\\ +\texttt{\qquad\qquad\ \^{}p[-3] \qquad\qquad\ \^{}p} +\end{center} + +\textbf{常见错误:} +\begin{itemize} + \item 认为\"下标不能是负数\"——这是对数组下标的误解。下标只是偏移量。 + \item 把 p[-3] 理解为\"从 p 往回数 3 个\"是对的,但计算具体是哪个元素时容易数错(\texttt{p} 指向 a[5],p[-1] → a[4],p[-2] → a[3],p[-3] → a[2])。 +\end{itemize} + +\textbf{讲师提示:} \textit{花 30 秒画一个数组内存布局图,让学生直观感受 p-3 移动的位置。强调 \"下标就是偏移量\" 这个本质。} + +{\color{highlight}答案:C(3)} + +% ------------------------------------------------------- +\subsection{第15题:联合体(union)大小} + +\textbf{概念概要:} union 的所有成员\textbf{共享同一块内存空间},大小等于最大成员的大小。 + +\textbf{核心讲解要点:} +\begin{itemize} + \item \texttt{unionexp\{int i; float j; double k;\} x;} 中:int 占 4 字节,float 占 4 字节,double 占 8 字节。 + \item union 只需分配足够容纳最大成员的空间即可:\texttt{max(4, 4, 8) = 8} 字节。 + \item \textbf{对比 struct:} struct 的大小 $\geq$ 各成员大小之和(还需考虑对齐填充)。例如 \texttt{struct\{int i; double k;\}} 通常是 16 字节(int 对齐到 8)。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 把 union 当 struct 算,把所有成员大小加起来——这是最经典的错误。 + \item 忽略内存对齐因素。实际上 union 在某些平台上也可能因对齐而稍大于最大成员,但本题的选项和常见计算机条件下答案就是 8。 +\end{itemize} + +\textbf{讲师提示:} \textit{可以用\"酒店房间\"类比:struct 是每人一间房,union 是所有客人共用一间最大的房。} + +{\color{highlight}答案:B(8)} + +% ------------------------------------------------------- +\subsection{第16题:结构体指针成员访问} + +\textbf{概念概要:} 结构体数组、结构体指针、成员访问运算符(\texttt{.} 与 \texttt{->})的正确用法。 + +\medskip +\textbf{讲师提示:} \textit{此题考查细节,要让学生理解\"数组名\"和\"结构体变量\"的本质区别。} + +\textbf{代码回顾:} +\begin{lstlisting}[language=C] +struct student { int num; int age; }; +struct student stu[3] = {{1001,20},{1002,19},{1004,20}}; +struct student *p; +p = stu; +\end{lstlisting} + +\textbf{逐选项分析:} +\begin{itemize} + \item A. \texttt{(*p).num}:解引用 p 得到结构体,再用 . 取成员。等价于 \texttt{p->num}。\textbf{正确。} + \item B. \texttt{(p++)->num}:p 先参与 -> 运算取 num,然后 p 自增指向 stu[1]。\textbf{正确。} + \item C. \texttt{p = stu.age}:\texttt{stu} 是数组名(类型为 \texttt{struct student[3]}),\underline{数组没有 .age 成员}!只有结构体变量/指针才能用 . 或 -> 取成员。\textbf{错误。} + \item D. \texttt{p++}:指针自增,p 从 stu[0] 移到 stu[1]。\textbf{正确。} +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 把数组名和结构体变量混淆——stu 是数组,stu[0] 才是结构体。 + \item 试图对数组名使用 .age——这是最初级的语法错误。 + \item 不清楚 \texttt{p++} 移动的距离:p 是 \texttt{struct student *},++ 会跳过一整个结构体的大小。 +\end{itemize} + +{\color{highlight}答案:C} + +% ------------------------------------------------------- +\subsection{第17题:for 循环条件分析} + +\textbf{概念概要:} 赋值运算符 \texttt{=} 与相等判断 \texttt{==} 的区别——C 语言中最经典也最危险的陷阱。 + +\medskip +\textbf{讲师提示:} \textit{这是高考频考点!几乎每届期末都会有一道题考 = 和 == 的混淆。} + +\textbf{代码分析:} +\begin{lstlisting}[language=C] +for (j = 0, k = -1; k = 1; j++, k++) + printf("*****\n"); +\end{lstlisting} + +\textbf{关键点:} +\begin{itemize} + \item for 的第二个表达式 \texttt{k = 1} 是\textbf{赋值},不是判断。在 C 中 \texttt{=} 运算符的返回值是\underline{被赋的值}。 + \item 赋值表达式 \texttt{k = 1} 的值为 1,在 C 中非 0 即为真(true)。 + \item 因此循环条件\textbf{永远为真}——这是无限循环。 + \item 如果写成 \texttt{k == 1},则初始 k=-1,条件为假,循环体一次也不执行。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 把 \texttt{k = 1} 读成\"判断 k 是否等于 1\"——这是视觉上的条件反射错误。 + \item 不清楚\"赋值表达式有值\"的概念——在 C 中,a = 5 这个表达式本身的值就是 5。 + \item 不知道 \texttt{j = 0, k = -1} 是逗号表达式:依次求值,最终值为 -1,但 for 的初始化部分只关心副作用(j=0, k=-1)。 +\end{itemize} + +\textbf{延伸:} 怎么避免这种错误?如果编译器不说,怎么办?——教学生把常量写在左边:\texttt{1 == k},一旦写成 \texttt{1 = k} 编译器会报错(因为不能给常量赋值)。但这个技巧在考试中不帮你解题,关键是\textbf{仔细读代码}。 + +{\color{highlight}答案:B(无限循环)} + +% ------------------------------------------------------- +\subsection{第18题:存储类别 static} + +\textbf{概念概要:} static 关键字对全局变量的作用——限制作用域(内部链接),不影响生命周期。 + +\textbf{核心讲解要点:} +\begin{itemize} + \item 题干关键词:\"\textbf{只允许本源文件所有函数使用}\"——这描述的是 static 修饰的全局变量。 + \item static 修饰全局变量/函数 → \textbf{内部链接}(internal linkage):仅在当前 .c 文件内可见,其他文件无法 extern 引用。 + \item 对比:不加 static 的全局变量默认是\textbf{外部链接}(external linkage),其他文件可通过 \texttt{extern} 声明后访问。 + \item auto:局部变量默认存储类别,离开作用域即销毁——与\"全局\"矛盾。 + \item register:建议编译器将变量放入寄存器(仅局部变量)——与\"全局\"矛盾。 + \item extern:声明外部变量,意在跨文件使用——与\"只允许本源文件\"矛盾。 +\end{itemize} + +\textbf{记忆口诀:} static 的两个含义(根据位置不同): +\begin{enumerate} + \item 修饰\textbf{局部变量} → 延长生命周期(函数调用间保值) + \item 修饰\textbf{全局变量/函数} → 限制可见性(仅本文件) +\end{enumerate} +二者本质相通:都是\"静态 + 私密\"。 + +\textbf{常见错误:} +\begin{itemize} + \item 混淆 static 修饰局部变量和全局变量的不同含义。 + \item 认为 static 全局变量\"不能修改\"——实际上可以在本文件内任意修改,只是外部不可见。 +\end{itemize} + +{\color{highlight}答案:C} + +% ------------------------------------------------------- +\subsection{第19题:字符串指针数组——类型判断} + +\textbf{概念概要:} 二维字符数组与指针的类型推导。 + +\textbf{代码分析:} +\begin{lstlisting}[language=C] +char s[][81] = {"Student","Teacher","Father","Mother"}, *ps = s[2]; +\end{lstlisting} + +\textbf{类型推导 — 从外到内:} +\begin{itemize} + \item \texttt{s}:由初始化列表推断为 \texttt{char[4][81]}——4 行,每行 81 个字符。 + \item \texttt{s[2]}:取第 3 行(索引从 0 开始),类型为 \texttt{char[81]}。 + \item 在初始化表达式中,\texttt{char[81]} \textbf{退化为} \texttt{char *}(指向首元素的指针)。 + \item 因此 \texttt{*ps} 的声明等价于:\texttt{ps} 是 \texttt{char *} 类型,初始化为指向 "Father" 的首字符。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 选 \texttt{char[81]}——没有理解数组退化为指针的规则。 + \item 选 \texttt{char **}——s[2] 是一维数组退化为指针,不是\"指针的指针\"。\texttt{char **} 对应的是 \texttt{s} 在某些上下文中的退化(s 退化为 \texttt{char (*)[81]},也不是 char **)。 +\end{itemize} + +\textbf{讲师提示:} \textit{强调\"数组名在表达式中退化为指向首元素的指针\"——这是 C 语言最核心的规则之一,贯穿整个指针体系。} + +{\color{highlight}答案:B(char *)} + +% ------------------------------------------------------- +\subsection{第20题:字符串指针输出} + +\textbf{概念概要:} 指针解引用的多层含义——\texttt{*ps} vs \texttt{ps} vs \texttt{*s[1]}。 + +\medskip +\textbf{讲师提示:} \textit{结合第 19 题的上下文,此题需要把 s[1]、ps、*ps 三者的输出格式串讲清楚。} + +\textbf{代码(基于第19题):} +\begin{lstlisting}[language=C] +printf("%c,%s,%c\n", *s[1], ps, *ps); +\end{lstlisting} + +\textbf{逐项分析:} +\begin{itemize} + \item \texttt{*s[1]}:\texttt{s[1]} 是 \texttt{"Teacher"}(类型 char[81],退化为 char*),\texttt{*} 解引用取首字符,得 \texttt{'T'}。格式符 \texttt{\%c},输出 \texttt{T}。 + \item \texttt{ps}:指向 \texttt{s[2]} = \texttt{"Father"} 的首字符。格式符 \texttt{\%s} 要求 char*,输出整个字符串 \texttt{Father}。 + \item \texttt{*ps}:解引用 ps,取 \texttt{"Father"} 的首字符,得 \texttt{'F'}。格式符 \texttt{\%c},输出 \texttt{F}。 +\end{itemize} + +\noindent 最终输出:\texttt{T, Father, F} + +\textbf{常见错误:} +\begin{itemize} + \item 把 \texttt{*s[1]} 当成 \"Teacher\"——忘记 \texttt{*} 解引用取的是单个字符。 + \item 把 \texttt{*ps} 当成 \"Father\"——同样的错误,\%c 输出的是字符而非字符串。 + \item 混淆 \texttt{\%s} 和 \texttt{\%c} 对参数的要求:\%s 需要指针(地址),\%c 需要字符值。 +\end{itemize} + +{\color{highlight}答案:A(T, Father, F)} diff --git a/note/chapters/20-basic-blank.tex b/note/chapters/20-basic-blank.tex new file mode 100644 index 0000000..29ebf2a --- /dev/null +++ b/note/chapters/20-basic-blank.tex @@ -0,0 +1,157 @@ +% ============================================ +% 基本概念填空题 +% ============================================ +\section{基本概念填空题} + +\subsection{第1空:源文件后缀名} + +\begin{itemize} + \item \textbf{考查知识点:} C 语言源文件的命名约定。 + \item \textbf{答案:} \texttt{\textbf{.c}} + \item \textbf{要点:} C 源文件以 \texttt{.c} 为后缀,头文件以 \texttt{.h} 为后缀。注意 \textbf{不是} \texttt{.cpp}(那是 C++),也不是 \texttt{.obj}(那是编译后的目标文件)。 + \item \textbf{常见错误:} 与 C++ 的 \texttt{.cpp} 混淆。考试时一定要看清题目问的是「C语言」还是「C++」。 +\end{itemize} + +\subsection{第2空:标准头文件} + +\begin{itemize} + \item \textbf{考查知识点:} \texttt{scanf} 函数声明的头文件。 + \item \textbf{答案:} \texttt{\textbf{stdio.h}} + \item \textbf{要点:} \texttt{stdio.h} = Standard Input/Output(标准输入输出)。\texttt{scanf}、\texttt{printf}、\texttt{getchar}、\texttt{putchar} 等都在其中。注意是 \texttt{stdio},不是 \texttt{studio}(很多同学拼错!)。 + \item \textbf{其他常见头文件:} + \begin{itemize} + \item \texttt{stdlib.h}:\texttt{malloc}、\texttt{free}、\texttt{atoi}、\texttt{rand} + \item \texttt{string.h}:\texttt{strlen}、\texttt{strcpy}、\texttt{strcmp} + \item \texttt{math.h}:\texttt{sqrt}、\texttt{pow}、\texttt{sin}、\texttt{cos} + \item \texttt{ctype.h}:\texttt{isalpha}、\texttt{isdigit}、\texttt{toupper} + \end{itemize} + \item \textbf{教学提示:} 建议学生记住这几个高频头文件的英文全称,帮助记忆——\texttt{stdio} 不是 \texttt{studio}。 +\end{itemize} + +\subsection{第3空:三种基本结构} + +\begin{itemize} + \item \textbf{考查知识点:} 结构化程序设计的三种基本结构。 + \item \textbf{答案:} \textbf{选择结构}(也称分支结构) + \item \textbf{三种结构:} 顺序结构 → \textbf{选择结构} → 循环结构 + \item \textbf{要点:} 这三种结构是1966年由Böhm和Jacopini证明的——任何程序都可以仅用这三种结构实现。选择结构在 C 语言中对应 \texttt{if-else} 和 \texttt{switch}。 + \item \textbf{常见错误:} 写成「分支结构」不算错,但如果题目填空处是给确定的空位,「选择」和「分支」哪个需要根据上下文判断。建议考试时以教材用词为准。 + \item \textbf{教学提示:} 此处可以简要点一下 goto 语句——它可以打破这三种结构,因此被广泛认为是有害的(Dijkstra, 1968 年著名论文 \textit{Go To Statement Considered Harmful})。 +\end{itemize} + +\subsection{第4空:取地址运算符} + +\begin{itemize} + \item \textbf{考查知识点:} 获取变量地址的运算符。 + \item \textbf{答案:} \texttt{\textbf{\&}} + \item \textbf{要点:} \texttt{\&} 运算符在 C 语言中有 \textbf{两种含义}: + \begin{enumerate} + \item 取地址运算符(一元):\texttt{\&a} → 变量 a 的地址,常配合 \texttt{scanf} 使用 + \item 按位与运算符(二元):\texttt{a \& b} → a 和 b 的按位与 + \end{enumerate} + \item \textbf{常见错误:} 把取地址写成 \texttt{*}(那是解引用/间接访问运算符,作用相反)。 + \item \textbf{教学提示:} 此处可追问:「\texttt{scanf("\%d", a)} 和 \texttt{scanf("\%d", \&a)} 有什么区别?」(前者把 a 的值当地址用,可能导致崩溃) +\end{itemize} + +\subsection{第5空:\texttt{y *= ++x} 复合赋值} + +\begin{itemize} + \item \textbf{考查知识点:} 前置自增 \texttt{++} 与复合赋值 \texttt{*=} 的 \textbf{运算顺序}。 + \item \textbf{答案:} \textbf{x = 6, y = 60}(顺序不可颠倒) + \item \textbf{逐步推导(给定 x = 5, y = 10):} + \begin{enumerate} + \item 先执行 \texttt{++x}:x 自增为 6 + \item 再执行 \texttt{y *= 6}:等价于 \texttt{y = y * 6 = 10 * 6 = 60} + \end{enumerate} + \item \textbf{常见错误:} 把运算顺序搞反——先算 \texttt{y *= x} 再自增,得到 y = 50, x = 6(错误)。\texttt{++x} 是前置自增,\textbf{先自增再使用}。 + \item \textbf{教学提示:} 此处可追问:「如果改成 \texttt{y *= x++} 结果是多少?」(x 先用再自增:y = 10 * 5 = 50, x = 6) +\end{itemize} + +\subsection{第6--7空:函数声明与函数指针} + +\begin{itemize} + \item \textbf{考查知识点:} 函数返回值类型识别 + 函数指针的声明语法。 + \item \textbf{第6空答案:} \texttt{\textbf{int}} + \item \textbf{第7空答案:} \texttt{\textbf{int (*p)(double *);}} + \item \textbf{要点分析:} + \begin{itemize} + \item 函数 \texttt{int f(double *p);} → 返回值为 \texttt{int},参数为 \texttt{double *} 类型 + \item 函数指针声明语法:\texttt{返回类型 (*指针名)(参数类型列表);} + \item 关键:\texttt{(*p)} 的括号 \textbf{不能省略}!否则 \texttt{int *p(double *)} 会变成「返回 \texttt{int*} 的函数 \texttt{p}」的声明。 + \end{itemize} + \item \textbf{常见错误:} + \begin{itemize} + \item 忘写括号:写成 \texttt{int *p(double *)} —— 语义完全不同(声明了一个返回 int* 的函数 p) + \item 参数类型写错:写成 \texttt{int (*p)(int)} —— 原函数参数是 \texttt{double *},不是 \texttt{int} + \end{itemize} + \item \textbf{教学提示:} 函数指针是考试的高频考点,建议学生记住「右左法则」(Right-Left Rule)或 spiral rule 来解读复杂声明。 +\end{itemize} + +\subsection{第8空:二维数组初始化} + +\begin{itemize} + \item \textbf{考查知识点:} 二维数组的初始化——按行填充,未初始化元素自动补 0。 + \item \textbf{答案:} \texttt{\textbf{11}} + \item \textbf{分析过程:} +\begin{lstlisting}[language=C] +int x[3][4] = {3,4,5,6,7,8,9,10,11,12}; +\end{lstlisting} + 按行优先顺序填充(每行 4 个元素): + \begin{itemize} + \item \texttt{x[0]}:\{3, 4, 5, 6\} + \item \texttt{x[1]}:\{7, 8, 9, 10\} + \item \texttt{x[2]}:\{11, 12, 0, 0\}(剩余位置补 0) + \end{itemize} + 因此 \texttt{x[2][0] = 11}。 + \item \textbf{常见错误:} + \begin{itemize} + \item 误以为 \texttt{x[2][0] = 12}(忽略了 \texttt{x[2]} 的第一个元素是第 9 个值,即 11) + \item 忘记未初始化部分自动补 0 + \end{itemize} + \item \textbf{教学提示:} 此处可用画图方式展示 3×4 矩阵,逐个填入值,帮助学生理解「行优先」存储。 +\end{itemize} + +\subsection{第9--10空:逗号表达式} + +\begin{itemize} + \item \textbf{考查知识点:} 逗号表达式的求值规则 + 赋值运算符的结合性。 + \item \textbf{第9空答案:} \texttt{\textbf{a = 6}} + \item \textbf{第10空答案:} \texttt{\textbf{i = 30}} + \item \textbf{原语句:} +\begin{lstlisting}[language=C] +int i, a; +i = (a = 2 * 3, a * 5), a + 6; +\end{lstlisting} + \item \textbf{逐步推导:} + \begin{enumerate} + \item \texttt{a = 2 * 3} → a = 6 + \item 内层逗号表达式 \texttt{(6, a * 5)} = \texttt{(6, 30)} → 值取最右 = 30 + \item \texttt{i = 30}(赋值运算符优先级高于逗号,所以先执行) + \item 最外层逗号:\texttt{(i = 30), a + 6} → 整体值为 12,但 \textbf{被丢弃} + \end{enumerate} + \item \textbf{关键要点:} + \begin{itemize} + \item 逗号表达式 \textbf{从左到右} 依次求值,最终值 = \textbf{最右边表达式} 的值 + \item 赋值运算符 \texttt{=} 的优先级 \textbf{高于} 逗号运算符(所以在 \texttt{a+6} 之前执行) + \item 括号改变了求值顺序:内层括号 \texttt{(a = 2*3, a*5)} 先被求值 + \end{itemize} + \item \textbf{常见错误:} + \begin{itemize} + \item 误以为整体表达式值为 12 并赋给 i(逗号表达式的值不一定赋给变量——除非显式赋值) + \item 忽略括号的作用,搞错求值顺序 + \end{itemize} + \item \textbf{教学提示:} 逗号表达式是期末考试 \textbf{必考} 的难点。建议让学生多练几道类似题目,理解「从左到右、值取最右」的规则。可以给出一个更简单的例子帮助理解: +\begin{lstlisting}[language=C] +int x; +x = (1, 2, 3); // x = 3 +\end{lstlisting} +\end{itemize} + +% ========== 小结与过渡 ========== +\subsection{小结} + +基本概念填空题考查的是 C 语言最基础的语法和概念:文件命名、头文件、三种结构、取地址、自增/复合赋值、函数指针、二维数组初始化和逗号表达式。 + +其中,\textbf{第5空(y *= ++x)}和 \textbf{第9--10空(逗号表达式)} 丢分最严重——核心原因是对「运算顺序」和「求值规则」理解不深。 + +\textbf{教学提示:} 此处请稍作停顿,询问学生是否有疑问。然后预告下一节内容:「下面我们进入阅读程序填空题,这部分考查大家对程序运行过程的理解能力。」 diff --git a/note/chapters/30-reading.tex b/note/chapters/30-reading.tex new file mode 100644 index 0000000..26ea451 --- /dev/null +++ b/note/chapters/30-reading.tex @@ -0,0 +1,281 @@ +% ============================================ +% 阅读程序填空题 Q1-5 讲稿 +% ============================================ +\section{阅读程序填空题(一)} + +% ------------------------------------------------------- +\subsection{第1题:for + continue + break} + +\textbf{概念概要:} for 循环中 \texttt{continue} 与 \texttt{break} 的行为差异——continue 跳过本轮剩余代码继续下一轮,break 直接退出循环。 + +\medskip +\textbf{讲师提示:} \textit{让学生先自己读代码,30 秒后在黑板上跟踪 j 和 i 的变化。务必一步一步来,不能跳步。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +int i = 1, j = 10; +for (; j >= 1; j--) { + if (j > 5) { i += 2; continue; } + if (j % 2) { j -= 2; break; } +} +printf("%d\t%d\n", i, j); +\end{lstlisting} + +\smallskip +\textbf{逐步执行追踪:} + +\begin{center} +\begin{tabular}{c|c|c|c|c} +\hline +\textbf{轮次} & \textbf{j} & \textbf{j > 5?} & \textbf{j \% 2?} & \textbf{i} \\ +\hline +1 & 10 & 是 → i+=2;\ continue & — & 3 \\ +2 & 9 & 是 → i+=2;\ continue & — & 5 \\ +3 & 8 & 是 → i+=2;\ continue & — & 7 \\ +4 & 7 & 是 → i+=2;\ continue & — & 9 \\ +5 & 6 & 是 → i+=2;\ continue & — & 11 \\ +6 & 5 & 否 & 1(真) → j-=2; \textbf{break} & 11 \\ +\hline +\end{tabular} +\end{center} + +\smallskip +\textbf{结果:} \texttt{i = 11, j = 3}(注意 j 在第 6 轮被改为 3 后才 break) + +\textbf{关键教学要点:} +\begin{itemize} + \item \texttt{j > 5} 时:j 从 10 到 6(共 5 次),每次 i+=2,i 从 1 变成 11。 + \item 同时 \texttt{continue} 跳过本轮后续代码——所以 \texttt{j \% 2} 的检查在 j>5 时从不执行。 + \item j=5 时才到达第二个 if:5 \% 2 = 1(真),执行 \texttt{j -= 2}(j 变为 3),然后 \texttt{break} 直接跳出循环。 + \item for 循环头部的 j--(递减)在 continue 跳过后仍会执行——这是 for 机制保证的。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 误以为 continue 会跳过 for 的 j--——实际上 for 循环的\"迭代表达式\"(j--)在 continue 后仍照常执行。 + \item 在 j=5 时忘记先执行 j-=2 再 break,误输出 j=5。 + \item 漏算 i 的累加次数:j=10,9,8,7,6 共 5 次,i=1+2×5=11。 +\end{itemize} + +{\color{highlight}输出:\texttt{11\quad 3}} + +% ------------------------------------------------------- +\subsection{第2题:指针参数} + +\textbf{概念概要:} 函数参数传递——值传递(形参有自己独立的存储空间)与指针传递(通过指针间接修改实参)。 + +\medskip +\textbf{讲师提示:} \textit{此题是区分\"值传递\"和\"地址传递\"的绝佳例子。重点强调:形参 x 是局部副本,修改它不影响实参,但通过指针 y 修改则会影响。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +void f1(int x, int *y) { x += *y; *y += x; } +int main() { + int x = 8, y = 6; + f1(y, &x); + printf("%d, %d\n", x, y); +} +\end{lstlisting} + +\smallskip +\textbf{逐步执行追踪:} + +\noindent \textbf{调用 f1(y, \&x)} → \texttt{f1(6, \&x)}: +\begin{enumerate} + \item 形参 x = 6(main 中 y 的值被\underline{复制}过来) + \item 形参 y 指向 main 的 x(地址为 \&main::x,当前值为 8) + \item \texttt{x += *y}:形参 x = 6 + 8 = \textbf{14}(注意:这是形参 x,是局部变量!) + \item \texttt{*y += x}:*y 即 main 的 x = 8 + 14 = \textbf{22}(通过指针修改了 main 中的 x) + \item f1 返回后:main 的 x = 22,main 的 y = 6(y 从未被修改——值传递) +\end{enumerate} + +\textbf{关键教学要点:} +\begin{itemize} + \item 形参 x 和 main 中 x 是\underline{两个不同的变量}——同名但不同作用域。 + \item 普通参数(int x)是值传递:函数内修改不影响实参。 + \item 指针参数(int *y)传递的是地址:通过 *y 可以间接修改实参。 + \item 调用时参数顺序是 \texttt{f1(y, \&x)},y 的值 6 传给形参 x,x 的地址传给形参 y——名字上有迷惑性,要让学生仔细核对。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 以为形参 x 就是 main 中的 x——因为同名!这是最容易犯错的点。 + \item 搞混哪个参数是值传递、哪个是指针传递——调用时 \texttt{f1(y, \&x)} 中 y 是值(6),\&x 是地址。 + \item 误以为 main 中的 y 也变了——实际 y 只是作为值传给了形参 x,main::y 始终为 6。 +\end{itemize} + +{\color{highlight}输出:\texttt{22, 6}} + +% ------------------------------------------------------- +\subsection{第3题:数组反转} + +\textbf{概念概要:} 原地(in-place)数组反转算法——对称交换。 + +\medskip +\textbf{讲师提示:} \textit{让学生用笔画出数组初始状态,然后逐步交换。这是典型的\"双指针\"思想的前身。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +void f2(int a[], int n) { + int i, t; + for (i = 0; i < n / 2; i++) { + t = a[i]; a[i] = a[n-1-i]; a[n-1-i] = t; + } +} +int main() { + int a[] = {5, 6, 7, 8}, i; + f2(a, 4); + for (i = 0; i < 4; i++) printf("%d", a[i]); +} +\end{lstlisting} + +\smallskip +\textbf{逐步执行追踪:} + +\noindent 初始:\texttt{a = [5, 6, 7, 8]},n = 4,n/2 = 2(循环 2 次) + +\begin{center} +\begin{tabular}{c|c|c|c|c} +\hline +\textbf{i} & \textbf{交换对} & \textbf{a[i]} ↔ \textbf{a[n-1-i]} & \textbf{结果} \\ +\hline +0 & a[0] ↔ a[3] & 5 ↔ 8 & [\textbf{8}, 6, 7, \textbf{5}] \\ +1 & a[1] ↔ a[2] & 6 ↔ 7 & [8, \textbf{7}, \textbf{6}, 5] \\ +\hline +\end{tabular} +\end{center} + +\noindent 最终数组:\texttt{[8, 7, 6, 5]},连续输出(无分隔符):\texttt{8765} + +\textbf{关键教学要点:} +\begin{itemize} + \item 循环次数:n/2。如果 n 是奇数,中间元素不需要交换——让学生验证 n=5 的情况。 + \item 对称下标:a[i] ↔ a[n-1-i]。 + \item 传数组名给函数实际传递的是\underline{首元素地址},因此函数内的修改会反映到 main 的数组。 + \item printf 没有加空格或换行,所以输出是连续的"8765"——阅读程序题要特别注意输出格式。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 循环写成 i < n(而非 n/2),导致交换两次又变回原数组。 + \item 忘记临时变量 t 的作用——没有 t 无法完成交换。 + \item 输出格式看错:以为输出带空格或换行。 +\end{itemize} + +{\color{highlight}输出:\texttt{8765}} + +% ------------------------------------------------------- +\subsection{第4题:字符处理} + +\textbf{概念概要:} 字符串逐字符比较与 ASCII 码运算——大小写转换(减 32)。 + +\medskip +\textbf{讲师提示:} \textit{先回顾 ASCII 表:'a'=97, 'A'=65,差值为 32。让学生记住这个差值——期末考试几乎所有字符题都绕不开它。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +char a[] = "abt", b[] = "acid", c[10]; int i = 0; +while (a[i] != '\0' && b[i] != '\0') { + if (a[i] >= b[i]) c[i] = a[i] - 32; + else c[i] = b[i] - 32; + ++i; +} +c[i] = '\0'; puts(c); +\end{lstlisting} + +\smallskip +\textbf{逐步执行追踪:} + +\begin{center} +\begin{tabular}{c|c|c|c|c|c|c} +\hline +\textbf{i} & \textbf{a[i]} & \textbf{b[i]} & \textbf{条件} & \textbf{取值} & \textbf{c[i] 计算} & \textbf{c[i] 字符} \\ +\hline +0 & 'a' (97) & 'a' (97) & a[i] ≥ b[i] ✓ & a[i] & 97-32=65 & 'A' \\ +1 & 'b' (98) & 'c' (99) & a[i] ≥ b[i] ✗ & b[i] & 99-32=67 & 'C' \\ +2 & 't' (116) & 'i' (105) & a[i] ≥ b[i] ✓ & a[i] & 116-32=84 & 'T' \\ +3 & '\textbackslash 0' & 'd' (100) & 循环条件不满足,退出 & — & — & — \\ +\hline +\end{tabular} +\end{center} + +\noindent 退出后 c[3] = '\textbackslash 0',c = \texttt{"ACT"}。\texttt{puts(c)} 输出 \texttt{ACT}。 + +\textbf{关键教学要点:} +\begin{itemize} + \item 循环遍历两个字符串,直到任一字符串遇到 '\textbackslash 0'。由于 a 只有 3 个字符("abt"),i=3 时退出。 + \item 逐位比较:取较大的字符转大写(小写字母 -32 = 大写)。这里 \"较大的字符\" 指 ASCII 码值更大。 + \item \texttt{puts(c)} 自动在末尾加换行,所以输出是 \texttt{ACT}(带换行,不影响结果)。 + \item 数组 a 和 b 长度不同(3 vs 4),循环在较短者结束时停止。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 字符比较时忘记用 ASCII 码——'a' (97) < 'c' (99),而不是按字母表\"位置\"比较。 + \item 忘记 c[i] = '\textbackslash 0' 这一句——没有它 puts 会输出到遇到 '\textbackslash 0' 为止,可能输出乱码。 + \item 减 32 的方向搞反——小写转大写是减 32,大写转小写是加 32。 +\end{itemize} + +{\color{highlight}输出:\texttt{ACT}} + +% ------------------------------------------------------- +\subsection{第5题:递归函数} + +\textbf{概念概要:} 递归函数的展开求值——自顶向下递推,自底向上回归。 + +\medskip +\textbf{讲师提示:} \textit{在黑板上画递归树!从 Fun(5,4) 开始,逐步向下展开,然后从叶子节点向上逐层返回。这是最直观的教学方式。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +int Fun(int a, int b) { + int p; + if (b < 1) p = 0; + else p = Fun(a - 1, b - 2) + a * a; + return p; +} +int main() { + int a = 5, b = 4; + printf("%d\n", Fun(a, b)); +} +\end{lstlisting} + +\smallskip +\textbf{递归展开过程:} + +\noindent 初始调用:\texttt{Fun(5, 4)} + +\noindent \textbf{第 1 层}:Fun(5, 4),b=4 ≥ 1 → \texttt{p = Fun(4, 2) + 5*5 = Fun(4, 2) + 25} + +\noindent \textbf{第 2 层}:Fun(4, 2),b=2 ≥ 1 → \texttt{p = Fun(3, 0) + 4*4 = Fun(3, 0) + 16} + +\noindent \textbf{第 3 层}:Fun(3, 0),b=0 < 1 → \texttt{p = 0}(\textbf{递归终止!}) + +\noindent \textbf{回溯第 2 层}:Fun(4, 2) = 0 + 16 = \textbf{16} + +\noindent \textbf{回溯第 1 层}:Fun(5, 4) = 16 + 25 = \textbf{41} + +\medskip +\textbf{关键教学要点:} +\begin{itemize} + \item 递归终止条件:b < 1 时 p = 0。参数 b 每次减 2,a 每次减 1。 + \item 注意:每一层都有自己的局部变量 p,互不影响。 + \item \texttt{a * a} 中的 a 是\textbf{当前层的形参 a}——第 1 层是 5(25),第 2 层是 4(16)。 + \item 递归执行流程:\"递\"到 Fun(3,0) 终止,然后\"归\"回去逐层加。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 把 Fun(a, b) 中 a*b 的和弄混——注意是 \texttt{a * a}(当前层的 a 的平方),不是 \texttt{a * b}。 + \item 在回溯时忘记加当前层的 a*a——只看到递归调用的结果。 + \item 搞错递归层次——分不清哪一层的 a 是多少。 +\end{itemize} + +\textbf{延伸:} 可以让学生思考——如果把递归终止条件换成 b == 0,会有什么不同?(Fun(3,0)仍然触发,但 Fun(3,1) 也会继续递归直到 b≤0。) + +{\color{highlight}输出:\texttt{41}} diff --git a/note/chapters/31-reading-2.tex b/note/chapters/31-reading-2.tex new file mode 100644 index 0000000..8587df5 --- /dev/null +++ b/note/chapters/31-reading-2.tex @@ -0,0 +1,324 @@ +% ============================================ +% 阅读程序填空题 Q6-10 讲稿 +% ============================================ +\section{阅读程序填空题(二)} + +% ------------------------------------------------------- +\subsection{第6题:static 局部变量} + +\textbf{概念概要:} static 修饰局部变量——\textbf{只初始化一次},值在函数调用间保留;作用域不变但生命周期延长至整个程序运行期。 + +\medskip +\textbf{讲师提示:} \textit{此题是理解 static 局部变量最重要的例题!先让学生回忆 static 的两种含义(第 18 题讲过的),再带入此题。务必强调\"static 局部变量只初始化一次\"。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +int k = 0, a = 1; +int f3(int c) { + static int a = 1; + return (++a) + c; +} +int main() { + { int a = 3; k += f3(a); } // 块1 + k += f3(a); // 块2 + printf("%d\n", k); +} +\end{lstlisting} + +\smallskip +\textbf{逐步执行追踪:} + +\noindent \textbf{初始化:} +\begin{itemize} + \item 全局 k = 0,全局 a = 1 + \item f3 中的 static a = 1(\textbf{只执行一次}) +\end{itemize} + +\noindent \textbf{块 1:} \texttt{\{ int a = 3; k += f3(a); \}} +\begin{enumerate} + \item 块内局部变量 a = 3(屏蔽全局 a) + \item 调用 f3(3):形参 c = 3 + \item \texttt{++a}:static a 从 1 变为 \textbf{2},返回 2 + 3 = \textbf{5} + \item k = 0 + 5 = \textbf{5} + \item 块结束,块内局部变量 a 销毁 +\end{enumerate} + +\noindent \textbf{块 2:} \texttt{k += f3(a);} +\begin{enumerate} + \item 块 1 的 a 已销毁,此处 a 是全局 a = \textbf{1} + \item 调用 f3(1):形参 c = 1 + \item \texttt{++a}:static a 从 2 变为 \textbf{3}(\underline{不重新初始化为 1!}),返回 3 + 1 = \textbf{4} + \item k = 5 + 4 = \textbf{9} +\end{enumerate} + +\noindent printf 输出 \textbf{9}。 + +\textbf{关键教学要点:} +\begin{itemize} + \item static 局部变量的\textbf{初始化只在程序加载时执行一次}。即使函数被多次调用,\texttt{static int a = 1;} 也只在第一次进入函数时执行。 + \item static 变量的值\textbf{在函数调用之间持久保留}——f3(3) 把 static a 改成 2,下次 f3(1) 从 2 开始。 + \item 作用域分析能力:块 1 的 a 局部变量 vs 全局 a vs f3 的 static a——三个不同的 a,互不干扰! +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 以为每次调用 f3 都会重新把 static a 初始化为 1——这是最常见的误解。 + \item 块 2 中 f3(a) 的 a 用错:以为还是块 1 的 a=3,实际块 1 的 a 已出作用域。 + \item 把 static a 和全局 a 混淆——两个变量虽然同名但作用域不同。 +\end{itemize} + +{\color{highlight}输出:\texttt{9}} + +% ------------------------------------------------------- +\subsection{第7题:结构体指针传参} + +\textbf{概念概要:} 结构体指针传参——通过指针直接修改原结构体成员;条件运算符(三元运算符)的求值。 + +\medskip +\textbf{讲师提示:} \textit{此题考察结构体指针操作 + 条件表达式。建议放慢速度,让学生跟着写每步的变化。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +struct Point { int x; int y; }; +void modifyPoint(struct Point *p) { + int temp = p->x; + p->x = p->y; + p->y = temp; + p->x = (p->x > 10) ? (p->x / 2) : (p->x * 2); +} +int main() { + struct Point myPoint = {5, 12}; + modifyPoint(&myPoint); + printf("x = %d, y = %d\n", myPoint.x, myPoint.y); +} +\end{lstlisting} + +\smallskip +\textbf{逐步执行追踪:} + +\noindent 初始:myPoint = \{x=5, y=12\} + +\noindent 调用 modifyPoint(\&myPoint),形参 p 指向 myPoint: + +\begin{center} +\begin{tabular}{c|l|c|c} +\hline +\textbf{步骤} & \textbf{操作} & \textbf{p->x} & \textbf{p->y} \\ +\hline +0 & 初始 & 5 & 12 \\ +1 & temp = p->x = 5 & 5 & 12 \\ +2 & p->x = p->y = 12(交换开始) & 12 & 12 \\ +3 & p->y = temp = 5(交换完成) & 12 & 5 \\ +4 & p->x = (12 > 10) ? (12/2) : (12*2) → 6 & \textbf{6} & 5 \\ +\hline +\end{tabular} +\end{center} + +\noindent 最终输出:\texttt{x = 6, y = 5} + +\textbf{关键教学要点:} +\begin{itemize} + \item \texttt{p->x} 等价于 \texttt{(*p).x}——通过指针访问结构体成员。 + \item 交换操作:先用 temp 保存 p->x 的原值(5),然后将 p->x 赋为 p->y(12),再将 p->y 赋为 temp(5)。 + \item 条件运算符 \texttt{(p->x > 10) ? (p->x / 2) : (p->x * 2)}:此时 p->x = 12,12 > 10 为真,执行前半部分:12 / 2 = 6。 + \item 由于传的是指针(\texttt{\&myPoint}),函数内对 *p 的任何修改都直接反映到 main 的 myPoint。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 交换步骤记错,导致最终 y 的值不对。 + \item 条件表达式判断时机——是在交换后的 p->x=12 时判断,不是初始的 5。 + \item 忘记传指针的效果——以为函数内修改不会影响 main(如果是值传递确实不影响,但这里传的是地址)。 +\end{itemize} + +{\color{highlight}输出:\texttt{x=6, y=5}} + +% ------------------------------------------------------- +\subsection{第8题:幂运算} + +\textbf{概念概要:} 用循环实现幂运算 $a^b$——累乘 b-1 次(因为 result 初始值为 a)。 + +\medskip +\textbf{讲师提示:} \textit{此题难度较低,可以快速讲。重点让学生注意边界条件 b=0 时返回 1 的处理。} + +\smallskip +\textbf{代码:} +\begin{lstlisting}[language=C] +int func(int a, int b) { + if (b == 0) return 1; + int result = a; + for (int i = 1; i < b; i++) result *= a; + return result; +} +int main() { + int x = 2, y = 3, z = func(x, y); + printf("z = %d\n", z); +} +\end{lstlisting} + +\smallskip +\textbf{逐步执行追踪:} + +\noindent 调用 func(2, 3): + +\begin{itemize} + \item b = 3 ≠ 0,不进入 if + \item result = a = \textbf{2} + \item 循环:i 从 1 到 2(i < b,即 i < 3) +\end{itemize} + +\begin{center} +\begin{tabular}{c|c|c} +\hline +\textbf{i} & \textbf{操作} & \textbf{result} \\ +\hline +— & 初始 & 2 \\ +1 & result *= 2 & 2 × 2 = 4 \\ +2 & result *= 2 & 4 × 2 = 8 \\ +\hline +\end{tabular} +\end{center} + +\noindent 返回 8,z = 8,输出 \texttt{z = 8}。 + +\textbf{关键教学要点:} +\begin{itemize} + \item 幂运算的本质:$2^3 = 2 \times 2 \times 2$。循环执行了 b-1=2 次乘法(初始已有 1 个 a)。 + \item 边界条件:b = 0 时,$a^0 = 1$,直接返回 1。 + \item 注意循环条件 \texttt{i < b}——如果写成 \texttt{i <= b} 会多乘一次,算出 $2^4 = 16$。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item 循环边界搞错:i=1;\ i='a' \&\& ch<='z') || (ch>='A' \&\& ch<='Z')}} —— 判断字符是否为英文字母。 + +\vspace{0.3em} +\textbf{这个空考什么:} 字符的ASCII码范围比较。C语言中字符本质上是一个整数(ASCII码值),所以可以直接用 \texttt{>=}、\texttt{<=} 比较。 + +\textbf{分两步理解:} +\begin{enumerate} + \item \texttt{ch>='a' \&\& ch<='z'} —— ch 的 ASCII 码在小写字母 a(97) 到 z(122) 之间。 + \item \texttt{ch>='A' \&\& ch<='Z'} —— ch 的 ASCII 码在大写字母 A(65) 到 Z(90) 之间。 + \item 用 \texttt{||} 连接 —— 因为字母包含大写和小写,两者是「或」的关系。 +\end{enumerate} + +\textbf{常见错误:} +\begin{itemize} + \item 写成 \texttt{'a'<=ch<='z'} —— \textbf{这是经典错误!}C语言不支持数学中的连续比较。\texttt{'a'<=ch<='z'} 实际上等价于 \texttt{('a'<=ch)<='z'},先算 \texttt{'a'<=ch} 得到 0 或 1,再和 \texttt{'z'} 比较,结果永远是 1,逻辑完全错误。 + \item 漏掉大写范围 —— 只写 \texttt{ch>='a' \&\& ch<='z'},会漏掉大写字母。 + \item 把 \texttt{||} 写成 \texttt{\&\&} —— 大写和小写不可能同时满足,用 \texttt{\&\&} 条件永远为假。 + \item 用 \texttt{isalpha(ch)} —— 函数调用确实可以,但需要 \texttt{\#include }。完善题一般考的是你手写判断的能力。 +\end{itemize} + +\vspace{0.6em} +\textbf{【3】\texttt{ch>='0' \&\& ch<='9'}} —— 判断字符是否为数字字符。 + +\vspace{0.3em} +\textbf{这个空考什么:} 字符数字的ASCII码范围。注意「数字字符」和「数值」的区别: +\begin{itemize} + \item \texttt{ch>='0' \&\& ch<='9'} —— 判断 ch 是字符 \texttt{'0'} 到 \texttt{'9'} + \item 不是判断 ch 本身的值在 0 到 9 之间! + \item 字符 \texttt{'0'} 的ASCII码是48,不是0。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item \texttt{ch>=0 \&\& ch<=9} —— 这是在比较ASCII码值 0\~{}9(控制字符),不是数字字符。 + \item \texttt{ch>=48 \&\& ch<=57} —— 语法上没错(这是数字字符的ASCII范围),但阅卷老师会认为你没有理解字符比较的本质。写 \texttt{'0'} 和 \texttt{'9'} 更清晰、更可读。 + \item 忘记引号,写成 \texttt{ch>=0 \&\& ch<=9} —— 同上。 +\end{itemize} + +\subsubsection{教学笔记} + +这道题的三个空分别考查了 \texttt{getchar()}、字母判断、数字判断,是C语言字符处理的基础三件套。 +考试中这类题频率很高,建议记住: +\begin{itemize} + \item 字母:\texttt{(ch>='a' \&\& ch<='z') || (ch>='A' \&\& ch<='Z')} + \item 数字:\texttt{ch>='0' \&\& ch<='9'} + \item 小写字母:\texttt{ch>='a' \&\& ch<='z'} + \item 大写字母:\texttt{ch>='A' \&\& ch<='Z'} + \item 大小写转换:\texttt{ch - 'a' + 'A'}(小写转大写) +\end{itemize} + +\textbf{教学提示:} 可以当场问学生:「\texttt{'5'} 减去 \texttt{'0'} 等于多少?」答案应该是 5 —— 字符 \texttt{'5'} 的 ASCII 码 53 减去 \texttt{'0'} 的 48 等于 5。这是字符转数字的常用技巧。 + +\bigskip +\bigskip + +% ==================================== +\subsection{第2题:数组查找(2空)} + +\textbf{考查知识点:} scanf 地址传递、数组元素比较、break 结束循环。 + +\subsubsection{题目原文} + +在一个包含10个整数的数组中,查找与给定值 x 相同的元素所在位置(下标)。 + +\subsubsection{代码还原} + +\begin{lstlisting}[language=C] +int a[10], i, x; +printf("input 10 integers:"); +for (i = 0; i < 10; i++) scanf("%d", 【4】); // 输入数组元素 +printf("input the number you want to find x:"); +scanf("%d", &x); +for (i = 0; i < 10; i++) + if (【5】) break; // 找到则跳出循环 +if (i < 10) + printf("the pos of x is: %d\n", i); +else + printf("can not find x!\n"); +\end{lstlisting} + +\subsubsection{逐空讲解} + +\textbf{【4】\texttt{\&a[i]}} —— scanf 需要变量的地址。 + +\vspace{0.3em} +\textbf{这个空考什么:} scanf 函数的取地址语法。这是初学者最易出错的知识点之一。 + +\textbf{核心概念:} +\begin{itemize} + \item scanf 的作用是把键盘输入的值写入变量,所以它需要知道变量在哪里——即变量的\underline{内存地址}。 + \item \texttt{\&} 是取地址运算符,\texttt{\&a[i]} 表示数组第 i 个元素的地址。 + \item 等价写法:\texttt{a+i}(数组名 a 是首地址,+i 偏移 i 个元素)。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item \texttt{a[i]} —— \textbf{最高频错误!} scanf 传的是值而不是地址。对于整型变量,scanf("\%d", a[i]) 会把 a[i] 的\underline{未初始化值}当作地址传给 scanf,导致段错误或写入非法内存。 + \item \texttt{a} —— 每次循环都给 scanf 传同一个地址 a(即 a[0] 的地址),结果所有输入都覆盖在 a[0] 上。虽然 \texttt{\&a[0]} 和 \texttt{a} 地址相同,但写成 a 就失去了循环的意义。 + \item \texttt{\&a} —— 取整个数组的地址(类型是 int(*)[10]),类型不对,虽然地址值可能相同但语义错误。 +\end{itemize} + +\textbf{记忆技巧:} scanf 的参数永远是\underline{地址}。数组元素用 \texttt{\&a[i]},单个变量用 \texttt{\&x},字符串用 \texttt{str}(数组名本身就是地址)。 + +\vspace{0.6em} +\textbf{【5】\texttt{a[i]==x}} —— 判断当前数组元素是否等于目标值。 + +\vspace{0.3em} +\textbf{这个空考什么:} 相等比较运算符。C 语言中 \texttt{==} 是比较,\texttt{=} 是赋值。 + +\textbf{为什么这里用 break:} +\begin{itemize} + \item 一旦找到目标值,i 就是目标的下标,不需要继续循环。 + \item break 直接跳出 for 循环,i 保留找到时的值。 + \item 后面的 \texttt{if (i < 10)} 判断就是利用了这个特性:如果正常找到了,i 一定 < 10;如果循环跑完也没 break,i 会是10。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item \texttt{a[i]=x} —— \textbf{致命错误!} 把 \texttt{x} 的值赋给了 \texttt{a[i]},同时赋值表达式的结果是 x 的值(非零即真),if 条件永远为真,第一次循环就会 break。修改了原数组数据,且逻辑完全错误。 + \item \texttt{a[i]==x \&\& break} —— break 不能放在表达式中,语法错误。 +\end{itemize} + +\subsubsection{教学笔记} + +这道题的核心是区分 \texttt{\&a[i]} 和 \texttt{a[i]} 在使用场景上的差异: +\begin{itemize} + \item scanf/\textbf{输入}时 —— 需要 \texttt{\&a[i]}(地址) + \item 计算/判断/\textbf{输出}时 —— 需要 \texttt{a[i]}(值) +\end{itemize} + +\textbf{教学提示:} 可以在黑板上画一个内存图,展示 a[0]\~{}a[9] 各占4字节,\texttt{\&a[0]} 指向第0个元素的起始地址。让学生直观理解「地址」和「值」的区别。 + +\bigskip +\bigskip + +% ==================================== +\subsection{第3题:统计非负数(5空)} + +\textbf{考查知识点:} 变量初始化、continue 跳过循环、累加器模式、printf 格式化输出。 + +\subsubsection{题目原文} + +读入20个整数,统计其中非负数的个数,并计算非负数之和。 + +\subsubsection{代码还原} + +\begin{lstlisting}[language=C] +int i, a[20], s, count; +s = 【6】; // 累加和初始化 +for (i = 0; i < 20; i++) scanf("%d", &a[i]); +for (i = 0; i < 20; i++) { + if (【7】) continue; // 跳过负数 + 【8】 // 累加非负数 + 【9】 // 计数加1 +} +printf("s = %d\t count = %d\n", 【10】); // 输出结果 +\end{lstlisting} + +\subsubsection{逐空讲解} + +\textbf{【6】\texttt{s=0}} —— 累加和变量初始化。 + +\vspace{0.3em} +\textbf{这个空考什么:} C 语言局部变量\underline{不会自动初始化为0}。如果不手动赋值,s 的值是内存中的垃圾数据,累加结果必然错误。 + +\textbf{常见错误:} +\begin{itemize} + \item 不填 —— 认为 s 默认就是 0,这是 \textbf{Java/Python 思维},不适用于 C。 + \item 写 \texttt{count=0} —— count 初始化也是需要的,但根据上下文,【6】的位置紧接 s,且后续代码中 count 在循环内才递增,所以【6】处先初始化 s。不过题目实际是给【6】处的 s 一个初始值。 +\end{itemize} + +\textbf{注意:} 观察第2行声明 \texttt{int i, a[20], s, count;} 之后 s 和 count 都没有被赋予初始值。在 C 语言中,非静态局部变量的初值是\underline{不确定}的。所以必须在使用前初始化。s 和 count 都需要初始化,本题中 s 在循环外初始化为0,count 在循环内通过 count++ 计数(但 count 本身也需要初始化为0,通常在声明后、循环前完成。这里的题目排版略有不严谨,但习惯上 s 和 count 都应初始化为0)。实际考试中注意观察代码上下文来确定是 s=0 还是 count=0。 + +\vspace{0.6em} +\textbf{【7】\texttt{a[i]<0}} —— 判断当前元素是否为负数。 + +\vspace{0.3em} +\textbf{这个空考什么:} continue 语句的用法 + 负数条件判断。 + +\textbf{逻辑分析:} +\begin{itemize} + \item 题目要求统计\underline{非负数}的个数和求和。 + \item 所以遇到负数应该\underline{跳过},不做任何处理。 + \item continue 的作用是结束本次循环迭代,直接进入 i++ 开始下一轮。 + \item 因此 if 的条件应该是「是负数」→ \texttt{a[i] < 0}。 +\end{itemize} + +\textbf{常见错误:} +\begin{itemize} + \item \texttt{a[i]>=0} —— 逻辑反了!如果条件写成非负数时才 continue,那么非负数被跳过、负数被统计,结果完全相反。 + \item \texttt{a[i]<=0} —— 把 0 也跳过了,但 0 是非负数,应该统计。 +\end{itemize} + +\vspace{0.6em} +\textbf{【8】\texttt{s+=a[i];} / \texttt{s=s+a[i];}} —— 累加非负数到总和。 + +\vspace{0.3em} +\textbf{这个空考什么:} 复合赋值运算符 \texttt{+=}(或者普通赋值加法)。 + +\textbf{位置分析:} continue 之后才执行到这里,意味着执行到【8】的元素一定是非负数。所以这里直接累加即可。 + +\textbf{常见错误:} +\begin{itemize} + \item \texttt{s = a[i]} —— 覆盖而非累加,最终 s 只会保存最后一个非负数的值。 + \item \texttt{s + a[i];} —— 表达式有值但没有赋值,s 不会改变。 +\end{itemize} + +\vspace{0.6em} +\textbf{【9】\texttt{count++;} / \texttt{++count;} / \texttt{count+=1;} / \texttt{count=count+1;}} —— 非负数计数加一。 + +\vspace{0.3em} +\textbf{这个空考什么:} 自增运算符 \texttt{++} 的用法。 + +\textbf{注意【8】和【9】的顺序:} 可以先累加再计数,也可以先计数再累加,不影响结果。考试中两空都给分(只要逻辑正确)。 + +\textbf{常见错误:} +\begin{itemize} + \item \texttt{count+1;} —— 没有赋值,count 不变。 + \item \texttt{count = 1;} —— 每次循环都把 count 设为 1,最终 count 永远是 1。 +\end{itemize} + +\vspace{0.6em} +\textbf{【10】\texttt{s, count}} —— printf 的参数列表。 + +\vspace{0.3em} +\textbf{这个空考什么:} printf 格式化字符串与参数的一一对应关系。 + +格式化串中有两个占位符:\texttt{\%d} 和 \texttt{\%d},分别对应格式串后的第1个和第2个参数。所以这里应填 \texttt{s, count}(注意顺序:先 s 后 count,与格式串中 \texttt{\%d} 出现的顺序一致)。 + +\textbf{常见错误:} +\begin{itemize} + \item \texttt{count, s} —— 格式串中 "s =" 在前、"count =" 在后,如果参数顺序颠倒,输出的数字会张冠李戴。 + \item \texttt{\&s, \&count} —— printf 不需要取地址!\textbf{scanf 要地址,printf 要值。}这是学生最容易混淆的一组知识点。 +\end{itemize} + +\subsubsection{教学笔记} + +这道题的5个空涵盖了变量初始化、循环控制、累加器模式、自增运算、格式化输出,构成了一个完整的「遍历数组 + 条件筛选 + 统计」范式。这个范式在 C 语言编程中非常常见,建议学生熟练掌握。 + +\textbf{教学提示:} 可以带领学生把代码逻辑用自然语言复述一遍:「初始化 s 为 0 → 读入20个数 → 遍历数组 → 如果当前数是负数就跳过(continue)→ 否则把它加到 s 上,count 加一 → 最后输出 s 和 count。」让学生养成「读代码→翻译成自然语言→理解逻辑」的习惯。 + +\bigskip +\bigskip + +% ==================================== +\subsection{程序完善题总结} + +\subsubsection{题型特点} + +程序完善题与阅读填空题的最大区别: +\begin{itemize} + \item 阅读填空题:代码是完整的,你在分析已有代码的运行结果。 + \item 完善题:代码有空白,你需要自己写出缺失的语句。 +\end{itemize} + +完善题考查的不是你「能不能看懂了」,而是你「会不会写」。 + +\subsubsection{答题策略} + +\begin{enumerate} + \item \textbf{先通读整段代码} —— 理解程序要完成什么功能。 + \item \textbf{观察空白周围代码} —— 从上下文推断空白处需要什么类型的语句。 + \item \textbf{确定空白的基本语法模式} —— 是函数调用(getchar)?是条件判断(if)?是赋值(=)?是输出(printf的参数)? + \item \textbf{填写后检查} —— 把填好的代码在脑中运行一遍,验证是否合理。 +\end{enumerate} + +\subsubsection{本大题考点汇总} + +\begin{table}[h] + \centering + \begin{tabular}{@{}cll@{}} + \toprule + \textbf{空号} & \textbf{答案} & \textbf{知识点} \\ + \midrule + 【1】 & \texttt{getchar()} & 字符输入函数 \\ + 【2】 & \texttt{(ch>='a'\&\&ch<='z')||...} & ASCII 范围判断 \\ + 【3】 & \texttt{ch>='0' \&\& ch<='9'} & 数字字符判断 \\ + 【4】 & \texttt{\&a[i]} & scanf 地址传递 \\ + 【5】 & \texttt{a[i]==x} & 相等比较(区分 = 和 ==) \\ + 【6】 & \texttt{s=0} & 变量初始化 \\ + 【7】 & \texttt{a[i]<0} & continue 跳过条件 \\ + 【8】 & \texttt{s+=a[i];} & 复合赋值 / 累加器模式 \\ + 【9】 & \texttt{count++;} & 自增运算符 / 计数模式 \\ + 【10】 & \texttt{s, count} & printf 参数对应 \\ + \bottomrule + \end{tabular} +\end{table} + +\textbf{教学提示:} 这个表可以在讲完后投影出来,让学生快速回顾所有考点。也可以提问学生:「哪个空你最容易填错?」根据反馈做针对性强调。 diff --git a/note/chapters/50-programming.tex b/note/chapters/50-programming.tex new file mode 100644 index 0000000..b76768a --- /dev/null +++ b/note/chapters/50-programming.tex @@ -0,0 +1,507 @@ +% ============================================ +% 五、编程题:电梯调度系统 — 讲稿 +% ============================================ +\section{编程题:电梯调度系统(10分)} + +这是本套模拟试卷中分值最高、综合性最强的题目。题目要求实现一个简化的电梯调度算法, +分三个子任务完成,模拟考试分值分配为 \textbf{4 + 2 + 4 = 10 分}。 + +\textbf{教学提示:} 这道题建议花 15\~{}20 分钟详细讲解。先讲清题目背景和输入输出规则(2分钟), +然后分别讲解三个子任务(每个 3\~{}4 分钟),最后带学生走一遍完整执行流程(3分钟)。 +强调「先想清楚再写代码」,算法思路比代码细节更重要。 + +\bigskip + +% ==================================== +\subsection{题目背景与输入输出} + +\subsubsection{场景描述} + +致远大厦有一部可停靠 1\~{}26 层楼的电梯。需要根据当前电梯位置、行驶方向和乘客按下的按钮, +决定电梯下一个应停靠的楼层。 + +\begin{itemize} + \item 电梯当前停在 a 楼,行驶方向为 d(1 = 上行,-1 = 下行)。 + \item 有 n 个按钮被按下(n 个整数,互不相同,不等于 a,不一定有序)。 + \item 按「就近原则」选择下一个停靠楼层。 +\end{itemize} + +\subsubsection{输入格式} + +\begin{lstlisting}[language=C] +第一行:n a d // n个按钮,a当前楼层,d方向 +第二行:n个整数 // 按钮楼层(乱序) +\end{lstlisting} + +\subsubsection{输出要求} + +\begin{enumerate} + \item 若 n = 0(无按钮按下),电梯停靠 1 楼,输出 \texttt{1}。 + \item 当前行駶方向有按钮 → 输出该方向最近的楼层。 + \item 当前行駶方向无按钮 → 反方向就近停靠(折返)。 +\end{enumerate} + +\subsubsection{分值分配} + +\begin{table}[h] + \centering + \begin{tabular}{@{}cll@{}} + \toprule + \textbf{部分} & \textbf{函数} & \textbf{分值} & \textbf{考点} \\ + \midrule + Part 1 & \texttt{my\_sort} & 4分 & 冒泡排序、数组参数传递 \\ + Part 2 & \texttt{find\_larger} & 2分 & 遍历查找、哨兵值 INT\_MAX \\ + Part 3 & \texttt{main} 调度逻辑 & 4分 & 方向判断、折返逻辑、函数调用与返回值处理 \\ + \bottomrule + \end{tabular} +\end{table} + +\textbf{教学提示:} 先让学生明确分值结构。Part 1 和 Part 2 是独立的基础函数, +即使 Part 3 调度逻辑写不出来,只要 sort 和 find 写对了也能拿到 6 分。 +所以考试策略上建议先确保 Part 1 和 Part 2 稳拿。 + +\bigskip +\bigskip + +% ==================================== +\subsection{Part 1:my\_sort — 排序函数(4分)} + +\subsubsection{函数原型与功能} + +\begin{lstlisting}[language=C] +void my_sort(int *arr, int cnt); +\end{lstlisting} + +对数组 \texttt{arr} 进行\underline{原地升序}排序,元素个数为 \texttt{cnt}。 + +\subsubsection{完整代码} + +\begin{lstlisting}[language=C] +void my_sort(int *arr, int cnt) { + for (int i = 0; i < cnt - 1; i++) { + for (int j = 0; j < cnt - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + int temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} +\end{lstlisting} + +\subsubsection{算法讲解 —— 冒泡排序} + +\textbf{核心思想:} 每一轮把「当前未排序部分的最大值」冒泡到最后。 + +\vspace{0.3em} +\textbf{外层循环 \texttt{i}:} 控制轮数。有 cnt 个元素,需要 cnt-1 轮比较。 +第 i 轮结束后,最后 i 个元素已排好序。 + +\textbf{内层循环 \texttt{j}:} 控制每轮内的比较次数。 +第 i 轮只需比较前 \texttt{cnt - i - 1} 对相邻元素(因为最后 i 个已就位)。 + +\textbf{交换逻辑:} 如果左侧元素 \texttt{arr[j]} 大于右侧元素 \texttt{arr[j+1]}, +则交换两者。需要一个临时变量 temp 完成交换。 + +\subsubsection{逐轮演示(以 \{5, 3, 8, 1\} 为例)} + +\begin{table}[h] + \centering + \begin{tabular}{@{}ccl@{}} + \toprule + \textbf{轮次 (i)} & \textbf{比较过程} & \textbf{结果} \\ + \midrule + i=0 (j=0\~{}2) & 5>3 交换, 5<8 不变, 8>1 交换 & \{3, 5, 1, {\color{highlight}8}\} \\ + i=1 (j=0\~{}1) & 3<5 不变, 5>1 交换 & \{3, 1, {\color{highlight}5}, 8\} \\ + i=2 (j=0) & 3>1 交换 & \{1, {\color{highlight}3}, 5, 8\} \\ + \bottomrule + \end{tabular} +\end{table} + +\textbf{教学提示:} 可以在黑板上画出每一步数组的变化,让学生直观看到最大值如何逐步冒泡到最后。 +这是很多同学第一次接触排序算法,图解比代码更有说服力。 + +\subsubsection{为什么需要排序?} + +这是理解整个电梯调度系统的\underline{关键前提}。 + +\textbf{原因:} 题目给定的按钮数组是\underline{乱序}的。 +而后续 Part 2 的 \texttt{find\_larger} 函数需要在数组中查找「大于 target 的最小值」。 +如果数组有序(升序),这个查找问题就变得简单 —— 只需从头遍历,找到第一个大于 target 的元素即可(因为后面的元素只会更大)。 + +当然,Part 2 中我们采用的是完整遍历法(不管有序无序都能找到), +但排序后的数组在逻辑上更清晰、便于理解「大于 target 的最小值」这个概念。 +同时,有序数组也为可能的二分查找优化留下了空间。 + +\textbf{另一个重要原因:} +当两个方向都可能有按钮时,有序数组让「最近楼层」的判断变得直接。 +\texttt{find\_larger} 返回的就是最近的上行目标,\texttt{find\_smaller} 返回的就是最近的下行目标。 + +\subsubsection{常见错误} + +\begin{itemize} + \item \textbf{内外循环边界写错:} \texttt{i < cnt}(应为 \texttt{cnt - 1})或 \texttt{j < cnt - 1}(应为 \texttt{cnt - i - 1})。虽然多跑几次循环不会出错,但浪费时间,体现对算法理解不深。 + \item \textbf{比较方向写反:} \texttt{if (arr[j] < arr[j+1])} —— 这就变成了降序排列。 + \item \textbf{交换时丢失数据:} 如果不用 temp 变量,\texttt{arr[j] = arr[j+1]; arr[j+1] = arr[j];} 会导致两个元素都变成同一个值(经典的 swap 陷阱)。 + \item \textbf{函数参数理解错误:} \texttt{int *arr} 传递的是数组首地址,函数内对 arr 的修改会影响原数组。这就是「原地排序」的实现基础。 +\end{itemize} + +\bigskip +\bigskip + +% ==================================== +\subsection{Part 2:find\_larger — 查找函数(2分)} + +\subsubsection{函数原型与功能} + +\begin{lstlisting}[language=C] +int find_larger(int *arr, int cnt, int target); +\end{lstlisting} + +在数组 \texttt{arr} 中查找大于 \texttt{target} 的最小元素。若不存在,返回 \texttt{-1}。 + +\subsubsection{完整代码} + +\begin{lstlisting}[language=C] +#include // 提供 INT_MAX + +int find_larger(int *arr, int cnt, int target) { + int min_larger = INT_MAX; // 初始化为最大整数,作为哨兵 + for (int i = 0; i < cnt; i++) { + if (arr[i] > target && arr[i] < min_larger) + min_larger = arr[i]; + } + if (min_larger == INT_MAX) return -1; // 未找到任何大于 target 的元素 + return min_larger; +} +\end{lstlisting} + +\subsubsection{分步讲解} + +\textbf{第1步:理解需求} + +题目描述为「找到比 target 大、但比数组中其他元素都小的元素」。 +翻译成人话:\textbf{在数组中,找大于 target 的最小值}。 + +例如,数组 \{2, 5, 8, 10\},target = 6 → 大于 6 的元素有 8 和 10,其中最小的是 8 → 返回 8。 +target = 12 → 没有大于 12 的元素 → 返回 -1。 + +\vspace{0.4em} +\textbf{第2步:哨兵值 INT\_MAX} + +\texttt{INT\_MAX} 是 \texttt{} 中定义的常量,值为 2147483647(在大多数平台上)。 +它的作用是作为 \texttt{min\_larger} 的初始值。 + +为什么不是 0 或 -1? +\begin{itemize} + \item 初始化为 0:如果数组中所有大于 target 的值都大于 0,逻辑没问题。但如果数组中有小于 0 的正数(比如大于 target 的值为 3),min\_larger 需要更新为 3,也没问题。但 0 不是一个好的哨兵值,因为如果所有大于 target 的元素都大于 INT\_MAX 呢?不可能,所以 INT\_MAX 是最好的哨兵。 + \item 初始化为 -1:-1 可能恰好是数组中大于 target 的最小值(比如 target = -2,数组中有 -1),导致 -1 永远不会被更新,因为 \texttt{arr[i] < min\_larger}(即 arr[i] < -1)不可能成立。 + \item 初始化为 INT\_MAX:INT\_MAX 大于任何可能的元素值,确保第一个符合条件的元素一定能更新 min\_larger。 +\end{itemize} + +\textbf{教学提示:} 「哨兵值」这种编程技巧在竞赛和考试中经常出现。选一个「绝对不会干扰比较结果」的初始值,确保算法正确性。 + +\vspace{0.4em} +\textbf{第3步:遍历与更新} + +\texttt{for} 循环遍历整个数组,对每个元素做两个判断: +\begin{enumerate} + \item \texttt{arr[i] > target} —— 这个元素是「上方」的吗? + \item \texttt{arr[i] < min\_larger} —— 这个元素比目前已知的最小值还小吗? +\end{enumerate} +两个条件同时满足 → 更新 min\_larger。 + +\vspace{0.4em} +\textbf{第4步:返回结果} + +循环结束后,检查 \texttt{min\_larger} 是否仍然是 INT\_MAX: +\begin{itemize} + \item 是 → 说明没有找到任何大于 target 的元素 → 返回 -1。 + \item 否 → 返回 min\_larger(即大于 target 的最小值)。 +\end{itemize} + +\subsubsection{find\_smaller 的对称性} + +题目声明了 \texttt{find\_smaller} 函数可直接调用,它是 \texttt{find\_larger} 的镜像: + +\begin{lstlisting}[language=C] +int find_smaller(int *arr, int cnt, int target) { + int max_smaller = INT_MIN; // 注意:初始化为最小整数 + for (int i = 0; i < cnt; i++) { + if (arr[i] < target && arr[i] > max_smaller) + max_smaller = arr[i]; + } + if (max_smaller == INT_MIN) return -1; + return max_smaller; +} +\end{lstlisting} + +\textbf{对称对比:} +\begin{table}[h] + \centering + \begin{tabular}{@{}lll@{}} + \toprule + \textbf{要素} & \textbf{find\_larger} & \textbf{find\_smaller} \\ + \midrule + 目标 & 大于 target 的最小值 & 小于 target 的最大值 \\ + 哨兵初始值 & INT\_MAX & INT\_MIN \\ + 判断条件 & \texttt{arr[i] > target} & \texttt{arr[i] < target} \\ + 更新条件 & \texttt{arr[i] < min\_larger} & \texttt{arr[i] > max\_smaller} \\ + 未找到返回值 & -1 & -1 \\ + \bottomrule + \end{tabular} +\end{table} + +\subsubsection{常见错误} + +\begin{itemize} + \item \textbf{哨兵值选错:} 用 0 或 -1 做初始值,导致结果被污染。 + \item \textbf{条件写反:} \texttt{arr[i] < target \&\& arr[i] < min\_larger} —— 小于 target 的是下行方向,与 find\_larger 的语义矛盾。 + \item \textbf{忘记头文件:} 使用 INT\_MAX 必须 \texttt{\#include }。 + \item \textbf{未处理「未找到」情况:} 忘记判断 min\_larger == INT\_MAX,直接返回 INT\_MAX 而非 -1。 +\end{itemize} + +\bigskip +\bigskip + +% ==================================== +\subsection{Part 3:main 函数 — 电梯调度逻辑(4分)} + +\subsubsection{完整代码} + +\begin{lstlisting}[language=C] +int main() { + int n, a, d; + scanf("%d %d %d", &n, &a, &d); + if (n == 0) { printf("1\n"); return 0; } // ① 无按钮→停1楼 + + int arr[100]; + for (int i = 0; i < n; i++) scanf("%d", &arr[i]); + my_sort(arr, n); // ② 排序按钮数组 + + int next_floor; + if (d == 1) { // ③ 上行方向 + next_floor = find_larger(arr, n, a); // 找上方最近的 + if (next_floor == -1) // 上方无按钮 + next_floor = find_smaller(arr, n, a); // 折返:下方最近的 + } else { // ④ 下行方向 + next_floor = find_smaller(arr, n, a); // 找下方最近的 + if (next_floor == -1) // 下方无按钮 + next_floor = find_larger(arr, n, a); // 折返:上方最近的 + } + printf("%d\n", next_floor); + return 0; +} +\end{lstlisting} + +\subsubsection{调度逻辑分步讲解} + +\textbf{教学提示:} 这是学生最容易丢分的部分。建议先用流程图或示意图讲清调度逻辑, +再对照代码逐行讲解。避免对着代码「念」—— 先讲算法,再讲实现。 + +\vspace{0.5em} +\textbf{步骤①:边界情况处理(无按钮)} + +\begin{lstlisting}[language=C] +if (n == 0) { printf("1\n"); return 0; } +\end{lstlisting} + +没有按钮按下时,电梯直接去 1 楼。这是题目要求的最简单情况。 + +\textbf{为什么 return 0 而不是用 else?} +一来避免后续代码嵌套过深,二来逻辑清晰:特殊情况先处理掉,后面的代码不需要再考虑 n=0 的情况。 +这种写法叫做「卫语句」(Guard Clause),是编程中常用的模式。 + +\vspace{0.5em} +\textbf{步骤②:排序} + +\begin{lstlisting}[language=C] +my_sort(arr, n); +\end{lstlisting} + +将乱序的按钮数组按升序排列。排序完成后: +\begin{itemize} + \item arr[0] 是最低楼层的按钮 + \item arr[n-1] 是最高楼层的按钮 + \item 数组变为有序,便于后续查找 +\end{itemize} + +\textbf{为什么有序很重要?} 对 find\_larger 来说,虽然遍历找最小值不要求有序, +但有序数组意味着「遍历到的第一个大于 target 的元素就是最小值」, +可以提前 break 优化性能。更重要的是,排序后调试和推理都更直观。 + +\vspace{0.5em} +\textbf{步骤③:上行方向 (d == 1)} + +\begin{lstlisting}[language=C] +next_floor = find_larger(arr, n, a); // 1) 先找上方最近的 +if (next_floor == -1) // 2) 上方没有? + next_floor = find_smaller(arr, n, a); // 3) 折返找下方最近的 +\end{lstlisting} + +\textbf{逻辑:} 上行时优先找\underline{上方}的按钮。如果上方没有按钮了(find\_larger 返回 -1), +说明所有按钮都在下方,电梯只能折返下行,去下方最近的楼层。 + +\vspace{0.5em} +\textbf{步骤④:下行方向 (d == -1)} + +\begin{lstlisting}[language=C] +next_floor = find_smaller(arr, n, a); // 1) 先找下方最近的 +if (next_floor == -1) // 2) 下方没有? + next_floor = find_larger(arr, n, a); // 3) 折返找上方最近的 +\end{lstlisting} + +与上行完全对称:下行时优先找\underline{下方}的按钮。如果下方没有,折返上行。 + +\subsubsection{调度逻辑图解} + +\begin{center} +\begin{tabular}{@{}c|c@{}} + \toprule + \textbf{上行 (d = 1)} & \textbf{下行 (d = -1)} \\ + \midrule + \texttt{find\_larger(arr, n, a)} & \texttt{find\_smaller(arr, n, a)} \\ + 找上方最近($>$ 且最小) & 找下方最近($<$ 且最大) \\ + $\downarrow$ 找到了?返回 & $\downarrow$ 找到了?返回 \\ + 找不到 $\rightarrow$ 折返 & 找不到 $\rightarrow$ 折返 \\ + \texttt{find\_smaller(arr, n, a)} & \texttt{find\_larger(arr, n, a)} \\ + 找下方最近 & 找上方最近 \\ + \bottomrule +\end{tabular} +\end{center} + +\textbf{教学提示:} 可以在黑板上画一个竖线代表楼层 1\~{}26,标注当前楼层 a,画箭头表示上行/下行方向, +再标出按钮位置。让学生直观看到「上方最近」和「下方最近」的含义。 + +\subsubsection{举例演示} + +\textbf{例1:当前5楼上行,按钮 \{2, 8, 10\}} + +排序后:\{2, 8, 10\} + +\begin{enumerate} + \item 方向 d=1(上行) + \item find\_larger(\{2,8,10\}, 3, 5):大于5的有 \{8, 10\},最小是 \textbf{8} + \item 找到了 → 输出 \textbf{8} +\end{enumerate} + +结论:电梯从5楼上行,最近的按钮在8楼 → 停在8楼。 + +\vspace{0.4em} +\textbf{例2:当前10楼上行,按钮 \{2, 3, 6\}} + +排序后:\{2, 3, 6\} + +\begin{enumerate} + \item 方向 d=1(上行) + \item find\_larger(\{2,3,6\}, 3, 10):大于10的元素不存在 → 返回 \textbf{-1} + \item 折返!find\_smaller(\{2,3,6\}, 3, 10):小于10的有 \{2, 3, 6\},最大是 \textbf{6} + \item 输出 \textbf{6} +\end{enumerate} + +结论:电梯在10楼往上的方向没有按钮了 → 改变方向下行 → 去下方最近的6楼。 + +\vspace{0.4em} +\textbf{例3:当前5楼下行,按钮 \{7, 10, 12\}} + +排序后:\{7, 10, 12\} + +\begin{enumerate} + \item 方向 d=-1(下行) + \item find\_smaller(\{7,10,12\}, 3, 5):小于5的元素不存在 → 返回 \textbf{-1} + \item 折返!find\_larger(\{7,10,12\}, 3, 5):大于5的有 \{7, 10, 12\},最小是 \textbf{7} + \item 输出 \textbf{7} +\end{enumerate} + +结论:电梯在5楼下行方向无按钮 → 改变方向上行驶 → 去上方最近的7楼。 + +\vspace{0.4em} +\textbf{例4:边界情况,n=0} + +不输入按钮,直接输出 1。电梯停到1楼。 + +\subsubsection{常见错误} + +\begin{itemize} + \item \textbf{忘记处理 n=0:} 直接读取 arr[i] 会出问题(没有第二行输入),程序可能卡住或读入垃圾数据。 + \item \textbf{忘记排序:} 直接对乱序数组调用 find\_larger/find\_smaller。虽然遍历法不依赖有序性,但排序是题目明确要求的步骤(Part 1 价值4分),不调用 my\_sort 会丢分。 + \item \textbf{方向判断反了:} d==1 时调 find\_smaller,d==-1 时调 find\_larger —— 完全搞反了上行和下行的语义。这是最严重的逻辑错误。 + \item \textbf{折返逻辑遗漏:} 只调用一次 find\_larger 或 find\_smaller,不判断 -1。当当前方向无按钮时,next\_floor 直接赋值为 -1 然后输出 -1,完全错误。必须加 if 判断并折返查找。 + \item \textbf{折返时调错函数:} 上行找不到 → 再次调 find\_larger(而不是 find\_smaller),等于再查一次没结果的查询,next\_floor 还是 -1。折返意味着换方向。 + \item \textbf{printf 输出多余内容:} 直接 printf("\%d", next\_floor) 即可,不需要 "the next floor is..." 之类的额外文字。OJ 判题只比对数字。 +\end{itemize} + +\bigskip +\bigskip + +% ==================================== +\subsection{完整执行流程演示} + +以 \textbf{输入 ``3 5 1''(n=3, a=5, d=1上行)}和 \textbf{按钮 ``10 2 8''} 为例,跟踪整个程序: + +\begin{center} +\begin{tabular}{@{}p{3cm}p{5cm}p{5cm}@{}} + \toprule + \textbf{代码位置} & \textbf{执行内容} & \textbf{程序状态} \\ + \midrule + scanf & 读入 n=3, a=5, d=1 & \\ + n==0? & n=3, 非0,继续 & \\ + 读入按钮 & arr = \{10, 2, 8\} & arr(乱序) \\ + my\_sort(arr, 3) & 冒泡排序 & arr = \{2, 8, 10\} \\ + d==1? & 是,执行上行分支 & \\ + find\_larger(\{2,8,10\}, 3, 5) & 遍历: 2>5? no; 8>5?\& 85?\& 10<8? no & 返回 8 \\ + next\_floor==-1? & 8 != -1, 不折返 & \\ + printf & 输出 8 & 输出: 8 \\ + \bottomrule +\end{tabular} +\end{center} + +\textbf{教学提示:} 可以让学生画出自己的测试用例,在纸上手动走一遍代码, +检查自己的程序是否正确。这是调试逻辑错误的终极方法。 + +\bigskip +\bigskip + +% ==================================== +\subsection{编程题总结} + +\subsubsection{本题的综合性} + +这道编程题融合了 C 语言的多个核心知识点: +\begin{enumerate} + \item \textbf{数组操作} —— 数组声明、遍历、元素访问、作为函数参数传递(传指针) + \item \textbf{排序算法} —— 冒泡排序的完整实现(经典算法入门) + \item \textbf{查找算法} —— 线性遍历 + 条件筛选 + 哨兵值技巧 + \item \textbf{分支逻辑} —— if-else 判断上行/下行方向、折返条件 + \item \textbf{函数设计与调用} —— 三个函数各司其职,main 负责调度编排 + \item \textbf{输入输出} —— scanf/printf 的格式化使用 +\end{enumerate} + +\subsubsection{考试策略建议} + +\begin{itemize} + \item \textbf{先拿下 6 分基础分:} 即使调度逻辑暂时想不清楚,先把 my\_sort 和 find\_larger 这两个独立函数写完。 + 它们是纯粹的算法题,不需要理解电梯调度即可完成。4分 + 2分 = 6分,已经超过编程题的一半。 + \item \textbf{画图辅助思考:} 调度逻辑涉及方向判断和折返,光靠脑想很容易晕。在草稿纸上画一条竖线标注楼层,把按钮和当前位置标出来,逻辑就清晰了。 + \item \textbf{检查边界情况:} n=0、上方无按钮、下方无按钮 —— 这三种特殊情况都要测试到。 + \item \textbf{注意返回值:} find\_larger 和 find\_smaller 找不到目标时返回 -1 而不是 0 或其他值。 + main 中必须判断 -1 才能触发折返逻辑。 +\end{itemize} + +\subsubsection{延伸思考(可选,面向学有余力的学生)} + +\begin{itemize} + \item \textbf{优化 find\_larger:} 数组已排序,可以改为二分查找,将时间复杂度从 O(n) 降到 O(log n)。 + \item \textbf{电梯规则的完善:} 真实电梯调度更加复杂——需要考虑多按钮按下时的顺序规划(SCAN算法 / LOOK算法)、同方向顺路停靠等。 + 学有余力的同学可以查阅操作系统中「磁盘调度算法」的资料,电梯调度和它非常相似。 + \item \textbf{升级为多电梯系统:} 如果有多部电梯,如何分配按钮?这涉及分布式调度问题,是面试中常见的设计题。 +\end{itemize} + +\textbf{教学提示:} 延伸思考部分视时间和学生水平决定是否展开。重点是确保所有学生都理解了基础版本。 + +\bigskip + +\begin{center} + \textbf{—— 编程题讲解结束 ——} +\end{center} diff --git a/note/main.tex b/note/main.tex new file mode 100644 index 0000000..a93a388 --- /dev/null +++ b/note/main.tex @@ -0,0 +1,80 @@ +% ============================================ +% C语言期末模拟卷讲评讲座 — 讲稿 +% 编译: xelatex note/main.tex +% ============================================ +\documentclass[UTF8,a4paper,11pt]{ctexart} + +% === 页面设置 === +\usepackage[top=2.5cm,bottom=2.5cm,left=2.5cm,right=2.5cm]{geometry} + +% === 中文支持 === +\usepackage{ctex} + +% === 超链接 === +\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} + +% === 代码 === +\usepackage{listings} +\lstset{ + language=C, + basicstyle=\small\ttfamily, + keywordstyle=\color{blue}\bfseries, + commentstyle=\color{gray}, + stringstyle=\color{teal}, + numbers=left, + numberstyle=\tiny, + frame=single, + breaklines=true, + showstringspaces=false, +} + +% === 颜色 === +\usepackage{xcolor} +\definecolor{primary}{RGB}{30,41,59} +\definecolor{accent}{RGB}{59,130,246} +\definecolor{highlight}{RGB}{217,119,6} + +% === 表格 === +\usepackage{array} +\usepackage{booktabs} + +% === 标题格式 === +\usepackage{titlesec} +\titleformat{\section}{\Large\bfseries\color{primary}}{}{0em}{} +\titleformat{\subsection}{\large\bfseries\color{primary}}{}{0em}{} + +% === 文档信息 === +\title{\Huge\bfseries C语言期末模拟卷讲评讲座} +\author{朋辈学业导师\\[0.3em]\footnotesize 南京理工大学~新生学院~致远书院} +\date{\today} + +\begin{document} + +\maketitle +\thispagestyle{empty} + +\newpage +\tableofcontents +\newpage + +% ========== 讲座开场 ========== +\input{chapters/00-opening} + +% ========== 选择题 ========== +\input{chapters/10-choice-1} +\input{chapters/11-choice-2} + +% ========== 基本概念填空题 ========== +\input{chapters/20-basic-blank} + +% ========== 阅读程序填空题 ========== +\input{chapters/30-reading} +\input{chapters/31-reading-2} + +% ========== 程序完善题 ========== +\input{chapters/40-complete} + +% ========== 编程题 ========== +\input{chapters/50-programming} + +\end{document} diff --git a/scripts/build-all.bat b/scripts/build-all.bat new file mode 100644 index 0000000..92f8064 --- /dev/null +++ b/scripts/build-all.bat @@ -0,0 +1,39 @@ +@echo off +echo ============================================ +echo C语言期末模拟卷讲评 - 完整构建 (PPT + 讲稿) +echo ============================================ +echo. + +REM === 构建幻灯片 === +echo [1/2] 正在编译幻灯片 (slide/main.tex) ... +cd /d "%~dp0..\slide" +latexmk -xelatex -interaction=nonstopmode main.tex +cd /d "%~dp0.." +if %errorlevel% equ 0 ( + echo √ 幻灯片编译成功 → slide\build\main.pdf +) else ( + echo × 幻灯片编译失败 + pause + exit /b 1 +) +echo. + +REM === 构建讲稿 === +echo [2/2] 正在编译讲稿 (note/main.tex) ... +cd /d "%~dp0..\note" +latexmk -xelatex -interaction=nonstopmode main.tex +cd /d "%~dp0.." +if %errorlevel% equ 0 ( + echo √ 讲稿编译成功 → note\build\main.pdf +) else ( + echo × 讲稿编译失败 + pause + exit /b 1 +) +echo. +echo ============================================ +echo 构建完成! +echo 幻灯片: slide\build\main.pdf +echo 讲 稿: note\build\main.pdf +echo ============================================ +pause diff --git a/scripts/build-all.ps1 b/scripts/build-all.ps1 new file mode 100644 index 0000000..353f9f3 --- /dev/null +++ b/scripts/build-all.ps1 @@ -0,0 +1,46 @@ +$ErrorActionPreference = "Stop" +Write-Host "============================================" -ForegroundColor Cyan +Write-Host " C语言期末模拟卷讲评 - 完整构建 (PPT + 讲稿)" -ForegroundColor Cyan +Write-Host "============================================" -ForegroundColor Cyan +Write-Host "" + +$root = "$PSScriptRoot\.." + +# === 构建幻灯片 === +Write-Host "[1/2] 正在编译幻灯片 (slide/main.tex) ..." -ForegroundColor Yellow +Push-Location "$root\slide" +latexmk -xelatex -interaction=nonstopmode main.tex +$slideOk = ($LASTEXITCODE -eq 0) +Pop-Location + +if ($slideOk) { + Write-Host "√ 幻灯片编译成功" -ForegroundColor Green + $slidePdf = Get-Item "$root\slide\build\main.pdf" + Write-Host " → slide\build\main.pdf ($([math]::Round($slidePdf.Length/1KB,1))KB)" -ForegroundColor Green +} else { + Write-Host "× 幻灯片编译失败" -ForegroundColor Red + exit 1 +} +Write-Host "" + +# === 构建讲稿 === +Write-Host "[2/2] 正在编译讲稿 (note/main.tex) ..." -ForegroundColor Yellow +Push-Location "$root\note" +latexmk -xelatex -interaction=nonstopmode main.tex +$noteOk = ($LASTEXITCODE -eq 0) +Pop-Location + +if ($noteOk) { + Write-Host "√ 讲稿编译成功" -ForegroundColor Green + $notePdf = Get-Item "$root\note\build\main.pdf" + Write-Host " → note\build\main.pdf ($([math]::Round($notePdf.Length/1KB,1))KB)" -ForegroundColor Green +} else { + Write-Host "× 讲稿编译失败" -ForegroundColor Red + exit 1 +} +Write-Host "" +Write-Host "============================================" -ForegroundColor Cyan +Write-Host " 构建完成!" -ForegroundColor Green +Write-Host " 幻灯片: slide\build\main.pdf" -ForegroundColor Green +Write-Host " 讲 稿: note\build\main.pdf" -ForegroundColor Green +Write-Host "============================================" -ForegroundColor Cyan diff --git a/scripts/clean-all.bat b/scripts/clean-all.bat new file mode 100644 index 0000000..1a0604e --- /dev/null +++ b/scripts/clean-all.bat @@ -0,0 +1,16 @@ +@echo off +echo ============================================ +echo 清理所有构建产物 +echo ============================================ +cd /d "%~dp0.." + +if exist slide\build ( + echo 删除 slide\build\ ... + rmdir /s /q slide\build +) +if exist note\build ( + echo 删除 note\build\ ... + rmdir /s /q note\build +) +echo 清理完成 +pause From e573648c4424dbf2db2b6ea0c6dc174ffc6f1424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 19:57:07 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=B2=E7=A8=BF?= =?UTF-8?q?=E7=BC=96=E8=AF=91=EF=BC=9A=E5=BC=95=E5=8F=B7+=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC+=E5=AE=8F=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- note/chapters/11-choice-2.tex | 52 ++++++++++++++++---------------- note/chapters/50-programming.tex | 2 +- note/main.tex | 6 ++++ 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/note/chapters/11-choice-2.tex b/note/chapters/11-choice-2.tex index b8559fa..2af0abf 100644 --- a/note/chapters/11-choice-2.tex +++ b/note/chapters/11-choice-2.tex @@ -10,18 +10,18 @@ \subsection{第11题:函数形参} \textbf{核心讲解要点:} \begin{itemize} \item A 正确:形参可以是任何类型——整型、指针、结构体、数组(退化为指针)等都合法。让学生回忆见过的各种函数声明。 - \item B 错误:\texttt{void} 函数完全可以有形参,例如 \texttt{void printf\_stars(int n);}。\texttt{void} 只是\"不返回值\",与形参无关。 - \item C 错误:形参名会\"屏蔽\"同名的全局变量。这是 C 语言的\textbf{名字遮蔽}机制——局部作用域优先。可以举例:在函数体内你无法访问被屏蔽的全局变量。 + \item B 错误:\texttt{void} 函数完全可以有形参,例如 \texttt{void printf\_stars(int n);}。\texttt{void} 只是”不返回值”,与形参无关。 + \item C 错误:形参名会”屏蔽”同名的全局变量。这是 C 语言的\textbf{名字遮蔽}机制——局部作用域优先。可以举例:在函数体内你无法访问被屏蔽的全局变量。 \item D 错误:无形参函数示例——\texttt{int getch(void);}、\texttt{int rand(void);}。 \end{itemize} \textbf{常见错误:} \begin{itemize} - \item 学生常以为 \texttt{void func()} 就是\"不能有参数\",实际上 \texttt{void} 只修饰返回类型。 - \item 混淆\"形参名遮蔽全局变量\"和\"不能同名\"——语法上允许同名,结果是屏蔽而非冲突。 + \item 学生常以为 \texttt{void func()} 就是”不能有参数”,实际上 \texttt{void} 只修饰返回类型。 + \item 混淆”形参名遮蔽全局变量”和”不能同名”——语法上允许同名,结果是屏蔽而非冲突。 \end{itemize} -\textbf{讲师提示:} \textit{此题为送分题,快速过。重点强调\"形参能屏蔽全局变量\"这个知识点,后面的阅读程序可能会遇到。} +\textbf{讲师提示:} \textit{此题为送分题,快速过。重点强调”形参能屏蔽全局变量”这个知识点,后面的阅读程序可能会遇到。} {\color{highlight}答案:A} @@ -46,7 +46,7 @@ \subsection{第12题:宏定义展开陷阱} \noindent \textbf{第3步}:$3 + (3 \times 3) + 3 = 3 + 9 + 3 = \mathbf{15}$ \bigskip -\textbf{为什么不是 36?} 学生直觉往往会\"先算 \texttt{F(3)} 得 6,再 $6 \times 6 = 36$\"。但宏不先计算——它只做文本粘贴。要想得到 36,宏定义必须加括号: +\textbf{为什么不是 36?} 学生直觉往往会”先算 \texttt{F(3)} 得 6,再 $6 \times 6 = 36$”。但宏不先计算——它只做文本粘贴。要想得到 36,宏定义必须加括号: \begin{lstlisting}[language=C] #define F(r) ((r)+(r)) // 安全的宏定义 @@ -56,7 +56,7 @@ \subsection{第12题:宏定义展开陷阱} \textbf{常见错误:} \begin{itemize} - \item 把宏当函数用,以为会\"先求值再替换\"——这是最根本的错误。 + \item 把宏当函数用,以为会”先求值再替换”——这是最根本的错误。 \item 忘记宏的副作用:\texttt{F(i++)} 展开为 \texttt{i++ + i++},i 自增两次,行为未定义! \item 不加括号导致的优先级问题不仅限于算术,逻辑运算符也一样(如 \texttt{\#define AND(a,b) a\&\&b} 写成 \texttt{AND(x, y)==1} 的歧义)。 \end{itemize} @@ -70,7 +70,7 @@ \subsection{第13题:函数的定义与调用嵌套} \textbf{核心讲解要点:} \begin{itemize} - \item \textbf{定义不可嵌套}:C 语言中所有函数定义都是\"平等\"的,不允许在一个函数体内定义另一个函数。这与 Pascal、Python 等语言不同。C 没有\"内部函数\"的概念。 + \item \textbf{定义不可嵌套}:C 语言中所有函数定义都是”平等”的,不允许在一个函数体内定义另一个函数。这与 Pascal、Python 等语言不同。C 没有”内部函数”的概念。 \item \textbf{调用可以嵌套}:函数 A 调用函数 B,B 调用函数 C——这是标准的嵌套调用。递归更是一种特殊的嵌套:函数调用自身。 \item 标准 C(C89/C99/C11)不支持嵌套函数定义。GCC 有扩展支持但\underline{不可移植},考试中视为非法。 \end{itemize} @@ -78,7 +78,7 @@ \subsection{第13题:函数的定义与调用嵌套} \textbf{常见错误:} \begin{itemize} \item 有 Python 基础的学生可能习惯在函数内定义辅助函数,会错误认为 C 也支持。 - \item 混淆\"嵌套调用\"与\"递归调用\"——递归是调用的特例,\"嵌套调用\"更宽泛。 + \item 混淆”嵌套调用”与”递归调用”——递归是调用的特例,”嵌套调用”更宽泛。 \end{itemize} {\color{highlight}答案:B(定义不可嵌套,调用可嵌套)} @@ -105,11 +105,11 @@ \subsection{第14题:指针下标运算} \textbf{常见错误:} \begin{itemize} - \item 认为\"下标不能是负数\"——这是对数组下标的误解。下标只是偏移量。 - \item 把 p[-3] 理解为\"从 p 往回数 3 个\"是对的,但计算具体是哪个元素时容易数错(\texttt{p} 指向 a[5],p[-1] → a[4],p[-2] → a[3],p[-3] → a[2])。 + \item 认为”下标不能是负数”——这是对数组下标的误解。下标只是偏移量。 + \item 把 p[-3] 理解为”从 p 往回数 3 个”是对的,但计算具体是哪个元素时容易数错(\texttt{p} 指向 a[5],p[-1] → a[4],p[-2] → a[3],p[-3] → a[2])。 \end{itemize} -\textbf{讲师提示:} \textit{花 30 秒画一个数组内存布局图,让学生直观感受 p-3 移动的位置。强调 \"下标就是偏移量\" 这个本质。} +\textbf{讲师提示:} \textit{花 30 秒画一个数组内存布局图,让学生直观感受 p-3 移动的位置。强调 “下标就是偏移量” 这个本质。} {\color{highlight}答案:C(3)} @@ -131,7 +131,7 @@ \subsection{第15题:联合体(union)大小} \item 忽略内存对齐因素。实际上 union 在某些平台上也可能因对齐而稍大于最大成员,但本题的选项和常见计算机条件下答案就是 8。 \end{itemize} -\textbf{讲师提示:} \textit{可以用\"酒店房间\"类比:struct 是每人一间房,union 是所有客人共用一间最大的房。} +\textbf{讲师提示:} \textit{可以用”酒店房间”类比:struct 是每人一间房,union 是所有客人共用一间最大的房。} {\color{highlight}答案:B(8)} @@ -141,7 +141,7 @@ \subsection{第16题:结构体指针成员访问} \textbf{概念概要:} 结构体数组、结构体指针、成员访问运算符(\texttt{.} 与 \texttt{->})的正确用法。 \medskip -\textbf{讲师提示:} \textit{此题考查细节,要让学生理解\"数组名\"和\"结构体变量\"的本质区别。} +\textbf{讲师提示:} \textit{此题考查细节,要让学生理解”数组名”和”结构体变量”的本质区别。} \textbf{代码回顾:} \begin{lstlisting}[language=C] @@ -192,8 +192,8 @@ \subsection{第17题:for 循环条件分析} \textbf{常见错误:} \begin{itemize} - \item 把 \texttt{k = 1} 读成\"判断 k 是否等于 1\"——这是视觉上的条件反射错误。 - \item 不清楚\"赋值表达式有值\"的概念——在 C 中,a = 5 这个表达式本身的值就是 5。 + \item 把 \texttt{k = 1} 读成”判断 k 是否等于 1“——这是视觉上的条件反射错误。 + \item 不清楚”赋值表达式有值”的概念——在 C 中,a = 5 这个表达式本身的值就是 5。 \item 不知道 \texttt{j = 0, k = -1} 是逗号表达式:依次求值,最终值为 -1,但 for 的初始化部分只关心副作用(j=0, k=-1)。 \end{itemize} @@ -208,12 +208,12 @@ \subsection{第18题:存储类别 static} \textbf{核心讲解要点:} \begin{itemize} - \item 题干关键词:\"\textbf{只允许本源文件所有函数使用}\"——这描述的是 static 修饰的全局变量。 + \item 题干关键词:”\textbf{只允许本源文件所有函数使用}”——这描述的是 static 修饰的全局变量。 \item static 修饰全局变量/函数 → \textbf{内部链接}(internal linkage):仅在当前 .c 文件内可见,其他文件无法 extern 引用。 \item 对比:不加 static 的全局变量默认是\textbf{外部链接}(external linkage),其他文件可通过 \texttt{extern} 声明后访问。 - \item auto:局部变量默认存储类别,离开作用域即销毁——与\"全局\"矛盾。 - \item register:建议编译器将变量放入寄存器(仅局部变量)——与\"全局\"矛盾。 - \item extern:声明外部变量,意在跨文件使用——与\"只允许本源文件\"矛盾。 + \item auto:局部变量默认存储类别,离开作用域即销毁——与”全局”矛盾。 + \item register:建议编译器将变量放入寄存器(仅局部变量)——与”全局”矛盾。 + \item extern:声明外部变量,意在跨文件使用——与”只允许本源文件”矛盾。 \end{itemize} \textbf{记忆口诀:} static 的两个含义(根据位置不同): @@ -221,12 +221,12 @@ \subsection{第18题:存储类别 static} \item 修饰\textbf{局部变量} → 延长生命周期(函数调用间保值) \item 修饰\textbf{全局变量/函数} → 限制可见性(仅本文件) \end{enumerate} -二者本质相通:都是\"静态 + 私密\"。 +二者本质相通:都是”静态 + 私密”。 \textbf{常见错误:} \begin{itemize} \item 混淆 static 修饰局部变量和全局变量的不同含义。 - \item 认为 static 全局变量\"不能修改\"——实际上可以在本文件内任意修改,只是外部不可见。 + \item 认为 static 全局变量”不能修改”——实际上可以在本文件内任意修改,只是外部不可见。 \end{itemize} {\color{highlight}答案:C} @@ -252,10 +252,10 @@ \subsection{第19题:字符串指针数组——类型判断} \textbf{常见错误:} \begin{itemize} \item 选 \texttt{char[81]}——没有理解数组退化为指针的规则。 - \item 选 \texttt{char **}——s[2] 是一维数组退化为指针,不是\"指针的指针\"。\texttt{char **} 对应的是 \texttt{s} 在某些上下文中的退化(s 退化为 \texttt{char (*)[81]},也不是 char **)。 + \item 选 \texttt{char **}——s[2] 是一维数组退化为指针,不是”指针的指针”。\texttt{char **} 对应的是 \texttt{s} 在某些上下文中的退化(s 退化为 \texttt{char (*)[81]},也不是 char **)。 \end{itemize} -\textbf{讲师提示:} \textit{强调\"数组名在表达式中退化为指向首元素的指针\"——这是 C 语言最核心的规则之一,贯穿整个指针体系。} +\textbf{讲师提示:} \textit{强调”数组名在表达式中退化为指向首元素的指针”——这是 C 语言最核心的规则之一,贯穿整个指针体系。} {\color{highlight}答案:B(char *)} @@ -283,8 +283,8 @@ \subsection{第20题:字符串指针输出} \textbf{常见错误:} \begin{itemize} - \item 把 \texttt{*s[1]} 当成 \"Teacher\"——忘记 \texttt{*} 解引用取的是单个字符。 - \item 把 \texttt{*ps} 当成 \"Father\"——同样的错误,\%c 输出的是字符而非字符串。 + \item 把 \texttt{*s[1]} 当成 “Teacher“——忘记 \texttt{*} 解引用取的是单个字符。 + \item 把 \texttt{*ps} 当成 “Father“——同样的错误,\%c 输出的是字符而非字符串。 \item 混淆 \texttt{\%s} 和 \texttt{\%c} 对参数的要求:\%s 需要指针(地址),\%c 需要字符值。 \end{itemize} diff --git a/note/chapters/50-programming.tex b/note/chapters/50-programming.tex index b76768a..04e24b7 100644 --- a/note/chapters/50-programming.tex +++ b/note/chapters/50-programming.tex @@ -45,7 +45,7 @@ \subsubsection{分值分配} \begin{table}[h] \centering - \begin{tabular}{@{}cll@{}} + \begin{tabular}{@{}clll@{}} \toprule \textbf{部分} & \textbf{函数} & \textbf{分值} & \textbf{考点} \\ \midrule diff --git a/note/main.tex b/note/main.tex index a93a388..95e5b1f 100644 --- a/note/main.tex +++ b/note/main.tex @@ -10,6 +10,12 @@ % === 中文支持 === \usepackage{ctex} +% === 文本符号 === +\usepackage{textcomp} + +% === 数学 === +\usepackage{amsmath,amssymb} + % === 超链接 === \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} From 78e248dd6299285c517a19e28422ede5e045c7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:26:56 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E4=BC=98=E5=8C=96=20build.ps1=EF=BC=9A?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B7=AF=E5=BE=84=E8=A7=A3=E6=9E=90=EF=BC=8C?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=BB=9D=E5=AF=B9=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/build.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/build.ps1 b/scripts/build.ps1 index ded073e..767547b 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -1,15 +1,17 @@ $ErrorActionPreference = "Stop" +$root = "$PSScriptRoot\.." Write-Host "=== C语言期末模拟卷讲评 - 编译 ===" -ForegroundColor Cyan Write-Host "" Write-Host "正在编译 slide\main.tex (xelatex) ..." -ForegroundColor Yellow -Push-Location -LiteralPath "$PSScriptRoot\..\slide" +Push-Location "$root\slide" latexmk -xelatex -interaction=nonstopmode main.tex +$ok = ($LASTEXITCODE -eq 0) Pop-Location -if ($LASTEXITCODE -eq 0) { +if ($ok) { Write-Host "" Write-Host "=== 编译成功 ===" -ForegroundColor Green - $pdf = Get-Item "slide\build\main.pdf" -ErrorAction SilentlyContinue; if (-not $pdf) { $pdf = Get-Item "..\slide\build\main.pdf" } - Write-Host "PDF: $($pdf.FullName) ($([math]::Round($pdf.Length/1KB,1))KB, $($pdf.LastWriteTime))" -ForegroundColor Green + $pdf = Get-Item "$root\slide\build\main.pdf" + Write-Host "PDF: slide\build\main.pdf ($([math]::Round($pdf.Length/1KB,1))KB)" -ForegroundColor Green } else { Write-Host "" Write-Host "=== 编译失败,请检查上方错误信息 ===" -ForegroundColor Red From 7d9ad8ae3915553d86533672aa084441140232cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:34:51 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20build-all.bat=20?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E7=BC=96=E7=A0=81=E9=97=AE=E9=A2=98=EF=BC=9A?= =?UTF-8?q?chcp=2065001=20+=20=E8=8B=B1=E6=96=87=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/build-all.bat | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/scripts/build-all.bat b/scripts/build-all.bat index 92f8064..46f9ae1 100644 --- a/scripts/build-all.bat +++ b/scripts/build-all.bat @@ -1,39 +1,40 @@ @echo off +chcp 65001 >nul echo ============================================ -echo C语言期末模拟卷讲评 - 完整构建 (PPT + 讲稿) +echo C Final Exam Review - Full Build (PPT + Notes) echo ============================================ echo. -REM === 构建幻灯片 === -echo [1/2] 正在编译幻灯片 (slide/main.tex) ... +REM === Build Slide === +echo [1/2] Building slide... cd /d "%~dp0..\slide" latexmk -xelatex -interaction=nonstopmode main.tex cd /d "%~dp0.." if %errorlevel% equ 0 ( - echo √ 幻灯片编译成功 → slide\build\main.pdf + echo [OK] Slide built: slide\build\main.pdf ) else ( - echo × 幻灯片编译失败 + echo [FAIL] Slide build failed pause exit /b 1 ) echo. -REM === 构建讲稿 === -echo [2/2] 正在编译讲稿 (note/main.tex) ... +REM === Build Note === +echo [2/2] Building note... cd /d "%~dp0..\note" latexmk -xelatex -interaction=nonstopmode main.tex cd /d "%~dp0.." if %errorlevel% equ 0 ( - echo √ 讲稿编译成功 → note\build\main.pdf + echo [OK] Note built: note\build\main.pdf ) else ( - echo × 讲稿编译失败 + echo [FAIL] Note build failed pause exit /b 1 ) echo. echo ============================================ -echo 构建完成! -echo 幻灯片: slide\build\main.pdf -echo 讲 稿: note\build\main.pdf +echo Build complete! +echo Slide: slide\build\main.pdf +echo Note: note\build\main.pdf echo ============================================ pause From 26d6c9f62bcd7f700b83228e7600ab7865cc6f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:38:19 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20GitHub=20Actions?= =?UTF-8?q?=EF=BC=9ALaTeX=20=E7=BC=96=E8=AF=91=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - push/PR 时自动编译 slide + note(texlive/texlive 镜像) - 检测 ! 错误行,失败时阻止合并 - 上传 PDF 为 artifact(保留 7 天) - 仅 slide/ note/ scripts/ 变更时触发 --- .github/workflows/latex-build.yml | 83 +++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/latex-build.yml diff --git a/.github/workflows/latex-build.yml b/.github/workflows/latex-build.yml new file mode 100644 index 0000000..66c7a4a --- /dev/null +++ b/.github/workflows/latex-build.yml @@ -0,0 +1,83 @@ +name: LaTeX Build Check + +on: + push: + paths: + - 'slide/**' + - 'note/**' + - 'scripts/**' + - '.github/workflows/latex-build.yml' + pull_request: + paths: + - 'slide/**' + - 'note/**' + - 'scripts/**' + +jobs: + build-slide: + name: Build Slide (xelatex) + runs-on: ubuntu-latest + container: + image: texlive/texlive:latest + steps: + - uses: actions/checkout@v4 + + - name: Build slide + working-directory: slide + run: latexmk -xelatex -interaction=nonstopmode main.tex + + - name: Check for errors + working-directory: slide + run: | + if grep -q "^!" build/main.log; then + echo "=== LaTeX Errors Found ===" + grep "^!" build/main.log + exit 1 + fi + + - name: Show result + working-directory: slide + run: | + grep "Output written" build/main.log + echo "Overfull vbox warnings: $(grep -c 'Overfull' build/main.log || echo 0)" + + - name: Upload slide PDF + uses: actions/upload-artifact@v4 + with: + name: slide + path: slide/build/main.pdf + retention-days: 7 + + build-note: + name: Build Note (xelatex) + runs-on: ubuntu-latest + container: + image: texlive/texlive:latest + steps: + - uses: actions/checkout@v4 + + - name: Build note + working-directory: note + run: latexmk -xelatex -interaction=nonstopmode main.tex + + - name: Check for errors + working-directory: note + run: | + if grep -q "^!" build/main.log; then + echo "=== LaTeX Errors Found ===" + grep "^!" build/main.log + exit 1 + fi + + - name: Show result + working-directory: note + run: | + grep "Output written" build/main.log + echo "Overfull vbox warnings: $(grep -c 'Overfull' build/main.log || echo 0)" + + - name: Upload note PDF + uses: actions/upload-artifact@v4 + with: + name: note + path: note/build/main.pdf + retention-days: 7 From 135bf75899cf5685ff9bde307d876d9beee6e79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:39:34 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20.gitignore=EF=BC=9A?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20images/=20=E5=92=8C=20products/=20?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=EF=BC=8C=E4=BF=9D=E7=95=99=20.sisyphus/=20?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=EF=BC=9B=E5=88=A0=E9=99=A4=20note/=20?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E4=B8=AD=E7=9A=84=20.gitkeep=20=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +++--- note/.gitkeep | 0 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 note/.gitkeep diff --git a/.gitignore b/.gitignore index b58b88b..f689627 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,8 @@ build/ *.pdf materials/ +images/ +products/ .opencode/ -.sisyphus/ - -images/ \ No newline at end of file +.sisyphus/ \ No newline at end of file diff --git a/note/.gitkeep b/note/.gitkeep deleted file mode 100644 index e69de29..0000000 From d8f859af99bdf2ee20e4e00028a419269e35eeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:42:14 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=E6=9E=84=E5=BB=BA=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20products/=20=E8=BE=93=E5=87=BA=E9=92=A9?= =?UTF-8?q?=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - build-all.bat/ps1 + build.bat/ps1 编译完成后自动复制 PDF - slide/build/main.pdf → products/展示.pdf - note/build/main.pdf → products/讲稿.pdf --- scripts/build-all.bat | 6 ++++++ scripts/build-all.ps1 | 7 +++++++ scripts/build.bat | 4 ++++ scripts/build.ps1 | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/scripts/build-all.bat b/scripts/build-all.bat index 46f9ae1..0f71e82 100644 --- a/scripts/build-all.bat +++ b/scripts/build-all.bat @@ -37,4 +37,10 @@ echo Build complete! echo Slide: slide\build\main.pdf echo Note: note\build\main.pdf echo ============================================ +echo. +echo === Copying to products/ === +if not exist "products" mkdir products +copy /y "slide\build\main.pdf" "products\展示.pdf" +copy /y "note\build\main.pdf" "products\讲稿.pdf" +echo Products: products\展示.pdf + products\讲稿.pdf pause diff --git a/scripts/build-all.ps1 b/scripts/build-all.ps1 index 353f9f3..735706c 100644 --- a/scripts/build-all.ps1 +++ b/scripts/build-all.ps1 @@ -44,3 +44,10 @@ Write-Host " 构建完成!" -ForegroundColor Green Write-Host " 幻灯片: slide\build\main.pdf" -ForegroundColor Green Write-Host " 讲 稿: note\build\main.pdf" -ForegroundColor Green Write-Host "============================================" -ForegroundColor Cyan + +# === 复制到 products/ === +$productsDir = "$root\products" +if (-not (Test-Path $productsDir)) { New-Item -ItemType Directory -Path $productsDir -Force | Out-Null } +Copy-Item "$root\slide\build\main.pdf" "$productsDir\展示.pdf" -Force +Copy-Item "$root\note\build\main.pdf" "$productsDir\讲稿.pdf" -Force +Write-Host "Products: products\展示.pdf + products\讲稿.pdf" -ForegroundColor Cyan diff --git a/scripts/build.bat b/scripts/build.bat index 69c2c37..50cadfc 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -9,6 +9,10 @@ if %errorlevel% equ 0 ( echo. echo === 编译成功 === echo PDF 已生成: slide\build\main.pdf + echo. + if not exist "products" mkdir products + copy /y "slide\build\main.pdf" "products\展示.pdf" + echo 已复制到: products\展示.pdf ) else ( echo. echo === 编译失败,请检查上方错误信息 === diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 767547b..04203e4 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -12,6 +12,10 @@ if ($ok) { Write-Host "=== 编译成功 ===" -ForegroundColor Green $pdf = Get-Item "$root\slide\build\main.pdf" Write-Host "PDF: slide\build\main.pdf ($([math]::Round($pdf.Length/1KB,1))KB)" -ForegroundColor Green + $productsDir = "$root\products" + if (-not (Test-Path $productsDir)) { New-Item -ItemType Directory -Path $productsDir -Force | Out-Null } + Copy-Item "$root\slide\build\main.pdf" "$productsDir\展示.pdf" -Force + Write-Host "Copied to: products\展示.pdf" -ForegroundColor Cyan } else { Write-Host "" Write-Host "=== 编译失败,请检查上方错误信息 ===" -ForegroundColor Red From e296136b3cc90555a8db59fd6876e2efce4f80d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:43:31 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=EF=BC=9A=E7=A7=BB=E9=99=A4=20build.bat=E3=80=81build.ps1=20?= =?UTF-8?q?=E5=92=8C=20clean.bat=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/build.bat | 20 -------------------- scripts/build.ps1 | 22 ---------------------- scripts/clean.bat | 11 ----------- 3 files changed, 53 deletions(-) delete mode 100644 scripts/build.bat delete mode 100644 scripts/build.ps1 delete mode 100644 scripts/clean.bat diff --git a/scripts/build.bat b/scripts/build.bat deleted file mode 100644 index 50cadfc..0000000 --- a/scripts/build.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -echo === C语言期末模拟卷讲评 - 编译 === -echo. -echo 正在编译 slide\main.tex (xelatex) ... -cd /d "%~dp0..\slide" -latexmk -xelatex -interaction=nonstopmode main.tex -cd /d "%~dp0.." -if %errorlevel% equ 0 ( - echo. - echo === 编译成功 === - echo PDF 已生成: slide\build\main.pdf - echo. - if not exist "products" mkdir products - copy /y "slide\build\main.pdf" "products\展示.pdf" - echo 已复制到: products\展示.pdf -) else ( - echo. - echo === 编译失败,请检查上方错误信息 === -) -pause diff --git a/scripts/build.ps1 b/scripts/build.ps1 deleted file mode 100644 index 04203e4..0000000 --- a/scripts/build.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -$ErrorActionPreference = "Stop" -$root = "$PSScriptRoot\.." -Write-Host "=== C语言期末模拟卷讲评 - 编译 ===" -ForegroundColor Cyan -Write-Host "" -Write-Host "正在编译 slide\main.tex (xelatex) ..." -ForegroundColor Yellow -Push-Location "$root\slide" -latexmk -xelatex -interaction=nonstopmode main.tex -$ok = ($LASTEXITCODE -eq 0) -Pop-Location -if ($ok) { - Write-Host "" - Write-Host "=== 编译成功 ===" -ForegroundColor Green - $pdf = Get-Item "$root\slide\build\main.pdf" - Write-Host "PDF: slide\build\main.pdf ($([math]::Round($pdf.Length/1KB,1))KB)" -ForegroundColor Green - $productsDir = "$root\products" - if (-not (Test-Path $productsDir)) { New-Item -ItemType Directory -Path $productsDir -Force | Out-Null } - Copy-Item "$root\slide\build\main.pdf" "$productsDir\展示.pdf" -Force - Write-Host "Copied to: products\展示.pdf" -ForegroundColor Cyan -} else { - Write-Host "" - Write-Host "=== 编译失败,请检查上方错误信息 ===" -ForegroundColor Red -} diff --git a/scripts/clean.bat b/scripts/clean.bat deleted file mode 100644 index 8cb6acc..0000000 --- a/scripts/clean.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off -echo === C语言期末模拟卷讲评 - 清理 === -cd /d "%~dp0.." -if exist slide\build ( - echo 正在删除 slide\build\ 目录... - rmdir /s /q slide\build - echo 清理完成 -) else ( - echo slide\build\ 目录不存在,无需清理 -) -pause From 8eeb1c7ccba2bc6ef5b93c07828be311adbeddcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:48:27 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=9A=E6=9B=B4=E6=96=B0=E6=9E=84=E5=BB=BA=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E3=80=81=E9=85=8D=E8=89=B2=E6=96=B9=E6=A1=88=E3=80=81=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AGENTS.md:95页、深蓝灰配色表、无 pause 的拆分 frame 结构 - AGENTS.md:添加 products/ + .github/ 到项目结构 - README.md:完整构建流程,包含 build-all.ps1 和 products/ 输出 - README.md:figures/ → images/,新增 .github/ 和 products/ - README.md:构建命令按推荐度排序(build-all 优先) --- AGENTS.md | 36 +++++++++++++++++++++--------------- README.md | 26 ++++++++++++++++---------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index df21131..1c34f6f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,15 +1,15 @@ # AGENTS.md — C语言期末模拟卷讲评讲座 -LaTeX beamer 幻灯片项目(96页),ctexbeamer + metropolis 主题,xelatex 编译。克莱因蓝 (IKB) 配色。 +LaTeX beamer 幻灯片项目(95页),ctexbeamer + metropolis 主题,xelatex 编译。深蓝灰 + 亮蓝 accent + 暖琥珀色答案高亮配色。 ## 编译命令 ```bash -cd slide && latexmk -xelatex main.tex # 编译到 slide/build/main.pdf -cd note && latexmk -xelatex main.tex # 编译到 note/build/main.pdf -scripts\build-all.bat # 一键构建 PPT + 讲稿 +cd slide && latexmk -xelatex main.tex # 编译幻灯片 → slide/build/main.pdf → products/展示.pdf +cd note && latexmk -xelatex main.tex # 编译讲稿 → note/build/main.pdf → products/讲稿.pdf +scripts\build-all.bat # 一键构建 PPT + 讲稿,并输出到 products/ +scripts\build-all.ps1 # PowerShell 一键构建 scripts\clean-all.bat # 清理所有构建产物 -python scripts\gen_note.py # 从 PDF 生成发言稿 speaker_notes.txt ``` - **必须 xelatex**,不可 pdflatex(中文支持) @@ -26,22 +26,24 @@ slide/chapters/ — 幻灯片 LaTeX 片段(不完整文档,由 slide/main. note/ — 讲稿 LaTeX 文件 (note/main.tex + note/chapters/) note/.latexmkrc — latexmk 编译配置(输出到 build/,即 note/build/) scripts/ — build-all.bat / build.ps1 / clean-all.bat / gen_note.py +products/ — 最终输出 PDF(展示.pdf + 讲稿.pdf,gitignore) +.github/ — GitHub Actions CI(latex-build.yml) materials/ — 原始试卷 PDF(gitignore) -figures/ — 图片资源 +images/ — 图片资源 code/ — 示例 C 代码 ``` ## 配色方案 -克莱因蓝 (International Klein Blue, RGB 0,47,167) 全蓝色系,定义在 `slide/main.tex` 前言区: +深蓝灰 + 亮蓝 accent + 暖琥珀色答案高亮,定义在 `slide/main.tex` 前言区: | 色名 | RGB | 用途 | |------|-----|------| -| primary | (0,38,150) | frametitle, block 标题, 进度条 | -| accent | (0,55,180) | 项目符号, 结构元素, 代码关键词 | -| highlight | (0,90,210) | 答案标注(`\color{highlight}`) | -| accentlight | (222,234,254) | block 背景 | -| bglight / bglighter | (244,247,254) / (250,251,255) | block body / code 背景 | +| primary | (30,41,59) | frametitle, block 标题, 进度条 | +| accent | (59,130,246) | 项目符号, 结构元素, 代码关键词 | +| highlight | (217,119,6) | 答案标注(`\color{highlight}`),暖琥珀色 | +| accentlight | (219,234,254) | block 背景 | +| bglight / bglighter | (241,245,249) / (248,250,252) | block body / code 背景 | 修改配色只需改 `slide/main.tex` 中 `\definecolor` 和 `\setbeamercolor` 块,不要散落到各 slide 文件。 @@ -65,11 +67,15 @@ code/ — 示例 C 代码 ### 标准 frame 结构 ```latex -\begin{frame}{题号标题}{知识点} +% --- 题目页 --- +\begin{frame}{题号标题}{知识点 --- 题目} \begin{exampleblock}{题目} 题目内容... \end{exampleblock} - \pause +\end{frame} + +% --- 解析页 --- +\begin{frame}{题号标题}{知识点 --- 解析} \begin{alertblock}{解析} 解析内容... {\color{highlight}答案:X} @@ -77,7 +83,7 @@ code/ — 示例 C 代码 \end{frame} ``` -- `\pause` 分离题目/解析,逐页揭晓 +- 题目与解析已拆分为独立 frame,不再使用 `\pause` - 答案用 `{\color{highlight}...}` 突出 - 代码块 `lstlisting` (language=C) diff --git a/README.md b/README.md index 09e0f79..715a683 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,12 @@ │ ├── clean-all.bat ← 清理所有构建产物 │ └── gen_note.py ← 从 PDF 提取文本生成发言稿 ├── materials/ ← 原始试卷 PDF(不纳入版本控制) -├── figures/ ← 图片资源 +├── images/ ← 图片资源(Logo 等) ├── code/ ← 示例代码 +├── products/ ← 最终输出 PDF(展示.pdf + 讲稿.pdf,gitignore) +├── .github/ ← GitHub Actions CI +│ └── workflows/ +│ └── latex-build.yml ← LaTeX 编译验证 ├── note/ ← 讲稿 LaTeX 文件 │ ├── main.tex ← 讲稿入口 │ ├── .latexmkrc ← 讲稿编译配置 @@ -137,20 +141,22 @@ ### 快速编译 ```bash -# 方式一:latexmk(推荐)— 仅幻灯片 -cd slide && latexmk -xelatex main.tex +# 完整构建 — 幻灯片 + 讲稿(推荐) +scripts\build-all.bat # Windows 批处理 +pwsh scripts\build-all.ps1 # PowerShell -# 方式二:完整构建 — 幻灯片 + 讲稿 -scripts\build-all.bat +# 仅幻灯片 +scripts\build.bat # Windows 批处理 +pwsh scripts\build.ps1 # PowerShell -# 方式三:仅幻灯片 -scripts\build.bat # 批处理 -pwsh scripts\build.ps1 # PowerShell +# 命令行(单独编译) +cd slide && latexmk -xelatex main.tex # 仅幻灯片 +cd note && latexmk -xelatex main.tex # 仅讲稿 ``` 编译成功后: -- 幻灯片 PDF 输出至 `slide/build/main.pdf` -- 讲稿 PDF 输出至 `note/build/main.pdf` +- `slide/build/main.pdf` → 复制到 `products/展示.pdf` +- `note/build/main.pdf` → 复制到 `products/讲稿.pdf` ### 清理 From ee5c7754640b437c12b6eb41af5f584580e887a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:49:05 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E8=AF=AF=E5=88=A0?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=EF=BC=9Abuild.bat,=20build.ps1,=20clean.bat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/build.bat | 20 ++++++++++++++++++++ scripts/build.ps1 | 22 ++++++++++++++++++++++ scripts/clean.bat | 11 +++++++++++ 3 files changed, 53 insertions(+) create mode 100644 scripts/build.bat create mode 100644 scripts/build.ps1 create mode 100644 scripts/clean.bat diff --git a/scripts/build.bat b/scripts/build.bat new file mode 100644 index 0000000..50cadfc --- /dev/null +++ b/scripts/build.bat @@ -0,0 +1,20 @@ +@echo off +echo === C语言期末模拟卷讲评 - 编译 === +echo. +echo 正在编译 slide\main.tex (xelatex) ... +cd /d "%~dp0..\slide" +latexmk -xelatex -interaction=nonstopmode main.tex +cd /d "%~dp0.." +if %errorlevel% equ 0 ( + echo. + echo === 编译成功 === + echo PDF 已生成: slide\build\main.pdf + echo. + if not exist "products" mkdir products + copy /y "slide\build\main.pdf" "products\展示.pdf" + echo 已复制到: products\展示.pdf +) else ( + echo. + echo === 编译失败,请检查上方错误信息 === +) +pause diff --git a/scripts/build.ps1 b/scripts/build.ps1 new file mode 100644 index 0000000..04203e4 --- /dev/null +++ b/scripts/build.ps1 @@ -0,0 +1,22 @@ +$ErrorActionPreference = "Stop" +$root = "$PSScriptRoot\.." +Write-Host "=== C语言期末模拟卷讲评 - 编译 ===" -ForegroundColor Cyan +Write-Host "" +Write-Host "正在编译 slide\main.tex (xelatex) ..." -ForegroundColor Yellow +Push-Location "$root\slide" +latexmk -xelatex -interaction=nonstopmode main.tex +$ok = ($LASTEXITCODE -eq 0) +Pop-Location +if ($ok) { + Write-Host "" + Write-Host "=== 编译成功 ===" -ForegroundColor Green + $pdf = Get-Item "$root\slide\build\main.pdf" + Write-Host "PDF: slide\build\main.pdf ($([math]::Round($pdf.Length/1KB,1))KB)" -ForegroundColor Green + $productsDir = "$root\products" + if (-not (Test-Path $productsDir)) { New-Item -ItemType Directory -Path $productsDir -Force | Out-Null } + Copy-Item "$root\slide\build\main.pdf" "$productsDir\展示.pdf" -Force + Write-Host "Copied to: products\展示.pdf" -ForegroundColor Cyan +} else { + Write-Host "" + Write-Host "=== 编译失败,请检查上方错误信息 ===" -ForegroundColor Red +} diff --git a/scripts/clean.bat b/scripts/clean.bat new file mode 100644 index 0000000..8cb6acc --- /dev/null +++ b/scripts/clean.bat @@ -0,0 +1,11 @@ +@echo off +echo === C语言期末模拟卷讲评 - 清理 === +cd /d "%~dp0.." +if exist slide\build ( + echo 正在删除 slide\build\ 目录... + rmdir /s /q slide\build + echo 清理完成 +) else ( + echo slide\build\ 目录不存在,无需清理 +) +pause From 3b7778930887826d9bc7c338531f327d8c8603df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Sun, 10 May 2026 21:50:50 +0800 Subject: [PATCH 13/13] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=EF=BC=9A=E5=88=A0=E9=99=A4=E7=A9=BA=E7=9A=84?= =?UTF-8?q?=20figures/=20=E5=92=8C=E4=BB=85=E6=9C=89=20.gitkeep=20?= =?UTF-8?q?=E7=9A=84=20code/=EF=BC=8C=E5=90=8C=E6=AD=A5=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 1 - README.md | 1 - code/.gitkeep | 0 3 files changed, 2 deletions(-) delete mode 100644 code/.gitkeep diff --git a/AGENTS.md b/AGENTS.md index 1c34f6f..182875f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,7 +30,6 @@ products/ — 最终输出 PDF(展示.pdf + 讲稿.pdf,gitignore) .github/ — GitHub Actions CI(latex-build.yml) materials/ — 原始试卷 PDF(gitignore) images/ — 图片资源 -code/ — 示例 C 代码 ``` ## 配色方案 diff --git a/README.md b/README.md index 715a683..54cf2db 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ │ └── gen_note.py ← 从 PDF 提取文本生成发言稿 ├── materials/ ← 原始试卷 PDF(不纳入版本控制) ├── images/ ← 图片资源(Logo 等) -├── code/ ← 示例代码 ├── products/ ← 最终输出 PDF(展示.pdf + 讲稿.pdf,gitignore) ├── .github/ ← GitHub Actions CI │ └── workflows/ diff --git a/code/.gitkeep b/code/.gitkeep deleted file mode 100644 index e69de29..0000000