流程控制那篇的猜数字程序里, 判断"大了小了"那段逻辑写了三遍. 输错要重来, 三次机会就是三段几乎一样的代码. 更早的第一个程序里其实已经见过函数了, main 就是一个, 只是当时没解释它凭什么叫"函数"
那篇里说过 一个 C 函数由首部 + 函数体两部分组成, 这个说法一直挂着没展开. 这篇就把它落实, 顺便解决"同样的代码写好几遍"这个问题
1 为什么需要函数
猜数字里判断大小的逻辑长这样:
1234567if (guess == answer) { printf("猜对了\n");} else if (guess > answer) { printf("大了\n");} else { printf("小了\n");}
逻辑本身不难, 但它只在"用户输入了一个数之后"才有意义. 如果程序多了个功能, 比如让用户按回车再猜一次, 或者记录每一轮的判断结果, 这 ...
前面几篇都在讲"怎么写对". 但真实情况是: 代码写完, 编译过了, 一跑结果不对. 这时候该怎么办?
这篇不按知识点排, 按报错的形状排, 从"编译器直接开骂"到"编译一声不吭但结果错了", 难度递增
0 综述
12345678910111213141516171819202122程序跑不对怎么办├── 1 三类错误├── 基础│ ├── 1 编译器报错怎么读│ ├── 2 中英文标点│ ├── 3 乱码│ ├── 4 少分号、多分号│ ├── 5 大括号不配对│ ├── 6 scanf 忘了 &│ ├── 7 格式符和类型不匹配│ └── 8 数组越界├── 进阶│ ├── 1 编译过了但结果不对│ ├── 2 变量没初始化│ ├── 3 整数除法与精度│ ├── 4 死循环怎么找│ └── 5 打印式调试└── 提高 ├── 1 学会看警告 ├── 2 二分定位法 ├── 3 崩溃了怎么查 └── 4 查错清单
1 三类错误
看到程序不对 ...
数据类型讲过, 一个变量就是一个盒子. 现在需要的是一排编好号的盒子, 用的时候拿编号去取. 这个东西就是数组
1 如何存 100 个数
回到猜数字那个需求. 现在想让它记录每一次猜的数, 最后一起打印出来. 按数据类型的写法:
1int guess1, guess2, guess3, guess4, guess5;
如果只猜 5 次, 忍忍也就过去了. 但需求一改成"最多猜 100 次", 这个写法立刻崩溃. 而且就算真写了 100 个变量, 后面想遍历它们也没办法, 变量名不能拼出来, 没法写 guess + i 来表示"第 i 个变量"
需要的东西满足三个条件: 一批同类型的盒子, 有连续的编号, 可以拿编号来访问. 这就是数组
1int guess[100]; // 100 个 int 盒子, 编号 0 到 99
2 一维数组的定义、初始化和引用
定义数组的写法是:
1类型 数组名[长度];
比如 int a[5]; 就是"5 个 int". 这里长度必须是常量, 不能是变量(考试环境按 C89, 不支持变长 ...
运算符与表达式最后那个计算器, 里面已经出现了 if 和 else if. 当时用得理所当然, 没解释为什么写成那样. 那篇的重点是"怎么算", 这篇要解决的是"算完之后, 程序该往哪儿走"
到目前为止写过的所有程序都有一个共同点: 从上到下, 一行一行执行到底, 中间不会拐弯. 哪怕计算器里有五个分支, 走的也是一条直线的形状, 写完一个 else if 就轮到下一个
真实程序不能这么死板. 例如输入密码要判断对错, 分数要分等级, 菜单要等用户选. 这些都需要程序能拐弯, 甚至回头. 让程序拐弯的语法, 就叫流程控制
0 综述
123456789101112131415161718流程控制├── 1 顺序结构├── 2 选择结构│ ├── 2.1 if│ ├── 2.2 非零即真│ ├── 2.3 if-else│ ├── 2.4 else if│ └── 2.5 switch├── 3 循环结构│ ├── 3.1 while│ ├── 3.2 do-while│ ├── 3.3 for│ ├── 3.4 ...
这是 F401 这一半的事. 接收 G431 吐过来的波形, 采下来, 认出是什么波, 再画到屏幕上
工程叫 Wave2, 代码都在 Core/Src 下
这块板子在干嘛
F401 拿到的是 G431 送过来的模拟信号. 它要干四件事
用 ADC 采下来, 每帧 500 点
从这 500 点里算出几个特征
靠特征判断这是什么波形
画到 OLED 上, 屏幕得能同时看到波形形状和识别结果
采集和 G431 是硬件同步的, 两边都用 100 kHz 触发, 所以采样率和信号频率是严格的整数比
1231 kHz 的信号, 100 kHz 采样每周期点数 = 100000 / 1000 = 100 点一帧 500 点 = 正好 5 个完整周期
5 个整周期这个数很舒服, 不整周期的话做频谱要加窗, 现在直接就能用
文件清单
文件
角色
关键内容
waveid.c
识别算法全部实现
特征提取 + 顺序判定, 不依赖任何 HAL 类型
app.c
业务逻辑
采集, 识别, 多数表决, 刷新节流, 按键
oled_ui.c
显示
波形绘图, 结果, 特征值
ad ...
本文只写 G431 这一半的事. 波形怎么造出来, 怎么发出去
F401 那边的采集和识别另开一篇
工程叫 Wave1, 代码都在 Core/Src 下
G431功能综述
G431 在这套系统里是个"信号源". 它的活儿很单纯: 吐波形, 而且要用一种接收端能对得上的方式吐.
最后定了 6 种: 正弦、方波、三角、锯齿、阶梯、调幅. 前四种是常规操作, 阶梯波是特意加的, 后面会说为什么. 调幅波是最后一轮补的, 它的意义和前五种不在一个层面上, 单独开一节讲.
最初的方案不是这样. 一开始正弦走 DAC, 方波走 TIM3 的硬件 PWM 从 PA6 出来, 两种波形两个引脚, 各走各的. 这个方案被我自己毙掉了, 原因在下面「所有波形都挤到一个引脚上」那一节.
文件清单
文件
角色
关键内容
wavegen.c
波形表生成
6 种波形, 全走 PA4, 改表即换波形
main.c
主入口
按键切波、串口命令、justfloat 打包发送
dac.c
DAC1 配置
G4 特有, 外设侧必须 WORD 对齐
adc.c
ADC1 配置 ...
数据类型那篇里, 我们让 5/2 得出了 0.000000 这个莫名其妙的结果, 当时说"格式符和类型不匹配". 但还有个更基础的问题, 考虑如下代码:
12345678#include <stdio.h>int main() { int a = 5, b = 2; double c = a/b; printf("%lf", c); // 输出 2}
上面这段代码里, c 是 double, printf 也用 %f 输出, 按理说该打印 2.5. 可实际结果是 2.000000, 问题出在 5/2 这一步, 它根本没算出 2.5
这就轮到运算符了
1 C 的运算符
打开任何一本教材的运算符表, 密密麻麻几十行, 第一反应是这牛魔要背到什么时候. 其实它们分成几类, 而且大部分早就会用, 只是不知道名字:
类别
运算符
干什么
算术
+ - * / %
加减乘除取余
关系
> < >= <= == !=
比大小, 结果是"真/假"
...
准备操作那篇里敲过一段代码, 当时说"目前不用知道原理". 现在该把那段代码拆开看一看了
这篇不打算先讲什么数据类型、什么运算符, 因为脱离代码去记这些名词没什么意义. 我们直接盯着一个能跑的程序, 一行一行搞清楚它在干什么. 等有了"一个C程序大概长什么样"的印象, 后面的东西才好往这个框架里塞
0 综述
123456789第一个程序├── 1 最小程序逐行拆解├── 2 头文件与 #include├── 3 main 函数├── 4 函数的形状├── 5 语句、分号与注释├── 6 printf 初步├── 7 scanf 初步└── 8 其他猎奇题目
1 最小程序逐行拆解
把准备操作里的那段拿来:
123456#include <stdio.h>int main() { printf("type in whatever you want to print"); return 0;}
四行代码, 去掉空行其实就四件事:
行
代码
在干什么
1
#include &l ...
观前提醒: 本章干货极多, 书中以表格展现为主, 本人认为这样反人类反直觉, 且极难学习, 故在本篇笔记中穿插了大量具体实例辅助理解, 同时对于一些结论性的内容辅以具体推算, 不要一口气吃完, 读读写写才有效
第一个程序里我们用 int age; 存过一个年龄, 但没解释那个 int 是什么意思. 这篇就来说清楚"数据往哪儿放、能放什么"
0 综述
1234567891011121314数据类型├── 1 为什么需要变量├── 2 变量的三个要素├── 3 命名规则与标识符├── 4 基本数据类型├── 5 类型修饰符├── 6 各类型占多少字节├── 7 常量├── 8 转义字符├── 9 类型转换├── 10 printf/scanf 格式符├── 11 单字符 IO├── 12 一个完整的例子└── *13 数据在内存中的存储形式
1 为什么需要变量
第一个程序最后的那个例子是这样的:
1234int age;printf("Input your age:");scanf("%d",&age);printf( ...
上一篇历史知识里提到存储器时只说了句”分内存、外存”.
但真到算容量、算地址、看八进制常量的时候, 这些概念是绕不开的,
所以单独开一篇.
这一篇在卷面上的权重不低: 进制转换几乎年年有,
地址与容量的计算题也常出.
而”各类存储介质的特点”这种纯背诵的内容近年考得很少, 书上却写得极多,
下面会明确标出哪些看一眼就行.
计算部分(地址与容量、进制互转)是必考的,
建议连例题一起动手算一遍; 概念部分只需记结论
0 综述
1234567891011存储器与进制├── 1 存储器基础(★★★★)│ ├── 存储单元与存储地址│ ├── 存储单位(bit/B/KB/MB/GB/TB)│ └── 内存与外存的区别├── 2 地址与容量计算(★★★★★)│ └── 地址总线根数 → 寻址范围├── 3 进制转换(★★★★★)│ ├── 十 ↔ N│ └── 二 ↔ 八 ↔ 十六(不必过十进制)└── 4 各类进制的用途与优缺点(★★)
1 存储器基础(★★★★)
1.1 存储单位
从小到大共六层. 相邻两级之间除 ...
博客快捷键
shift K
关闭快捷键功能
shift A
打开/关闭中控台
shift M
播放/暂停音乐
shift D
深色/浅色显示模式
shift S
站内搜索
shift R
随机访问
shift H
返回首页
shift F
友链鱼塘
shift L
友链页面
shift P
关于本站
shift I
原版/本站右键菜单
数据库加载中
通知
你好呀











