3. C语言基础回顾(上):数据类型、运算符、流程控制、循环
各位同学,欢迎来到第三章。我知道很多人一听到「回顾基础」就觉得无聊,恨不得直接跳到项目实战。但别急,我做了十几年嵌入式,见过太多人因为基础不牢,在调试时浪费大量时间。今天这堂课,咱们把C语言最核心的几个零件拆开看看。
说白了,嵌入式开发就是跟硬件打交道。而C语言,就是我们指挥硬件的「遥控器」。你想想看,如果遥控器上的按键你都搞不清楚,那怎么控制设备?
3.1 数据类型:给变量找个合适的「家」
在嵌入式世界里,内存是稀缺资源。一块STM32芯片的RAM可能只有几十KB,比一张老照片还小。所以,选对数据类型特别重要。
我个人习惯,在定义变量前先问自己三个问题:这个数会不会是负数?最大能到多少?需要小数吗?
| 数据类型 | 占用字节 | 取值范围 | 典型用途 |
|---|---|---|---|
| char | 1 | -128 ~ 127 | 存储字符、小范围状态 |
| unsigned char | 1 | 0 ~ 255 | 传感器原始数据(如ADC值) |
| int | 4 | -21亿 ~ 21亿 | 普通计数器、循环变量 |
| unsigned int | 4 | 0 ~ 42亿 | 时间戳、大范围计数 |
| float | 4 | ±3.4e-38 ~ ±3.4e38 | 温度、电压等小数数据 |
这里有个小技巧:在嵌入式开发中,我建议多用unsigned char来存储传感器数据。为什么?因为很多传感器的输出范围就是0-255,用char刚好,省内存。
3.2 运算符:C语言的「计算器」
运算符这东西,看着简单,但用不好容易出bug。我把它分成三类来讲。
3.2.1 算术运算符
加减乘除大家都懂,但有个坑——整数除法。你试试5 / 2,结果是2,不是2.5。因为两个整数相除,结果还是整数,小数部分直接丢掉。
int a = 5, b = 2;
float c;
c = a / b; // c = 2.0,不是2.5!
c = (float)a / b; // 这样才是2.5
3.2.2 关系运算符与逻辑运算符
关系运算符就是>、<、==这些。逻辑运算符是&&(与)、||(或)、!(非)。
嗯,这里要注意一个经典错误:把==写成=。比如:
if (x = 5) { // 这是赋值,不是判断!
// 这里的代码永远会执行
}
为什么会这样?因为x = 5这个表达式的结果是5,而5在C语言中被视为「真」。所以条件永远成立。我个人习惯把常量写在左边:if (5 == x),这样万一写成=,编译器会报错。
3.2.3 位运算符:嵌入式开发的「独门武器」
位运算在嵌入式里太常用了。说白了,就是直接操作二进制位。常用的有:
&按位与:清零某位|按位或:置位某位^按位异或:翻转某位~按位取反:全部取反<<左移:相当于乘以2>>右移:相当于除以2
// 假设我们要设置GPIO端口的第3位为1
unsigned char port = 0b00000000;
port |= (1 << 3); // port变成0b00001000
// 清零第3位
port &= ~(1 << 3); // port变回0b00000000
3.3 流程控制:让程序「有脑子」
程序不是一直顺序执行的,它需要根据条件做决策。这就是流程控制的作用。
3.3.1 if/else:最常用的判断
if/else的语法很简单,但嵌套多了容易乱。我见过有人写了七八层if嵌套,那代码简直没法看。
if (温度 > 50) {
// 开启风扇
} else if (温度 > 30) {
// 开启散热片
} else {
// 正常状态,什么都不做
}
3.3.2 switch:多分支的「高速公路」
当判断条件很多时,switch比if/else更清晰。比如根据按键值执行不同操作:
switch (key_value) {
case 0x01:
// 启动
break;
case 0x02:
// 停止
break;
case 0x03:
// 复位
break;
default:
// 无效按键
break;
}
注意每个case后面都要加break,不然会「穿透」到下一个case。不过有时候,穿透也是一种技巧。比如多个case执行相同操作时:
switch (mode) {
case 1:
case 2:
// 模式1和2执行相同操作
break;
case 3:
// 模式3单独处理
break;
}
3.4 循环:让程序「不知疲倦」
嵌入式程序大部分时间都在循环。比如不断读取传感器数据、不断刷新显示屏。
3.4.1 for循环:适合「知道次数」的场景
for循环的结构是:初始化、条件判断、更新。比如让LED闪烁10次:
for (int i = 0; i < 10; i++) {
LED_ON();
delay(500);
LED_OFF();
delay(500);
}
我个人习惯把循环变量定义在for内部,这样出了循环就释放了,省内存。
3.4.2 while循环:适合「不知道次数」的场景
while循环只判断条件,条件为真就一直执行。比如等待按键松开:
while (按键按下) {
// 等待,什么都不做
}
// 按键松开了,继续执行
还有一种do...while,它至少执行一次。我用的不多,但在某些场景下很实用,比如先发送一次数据,再检查是否成功。
while(1)然后忘了加退出条件,程序就卡死了。如果确实需要无限循环,记得在里面加个break条件。
3.5 实战小例子:温控风扇
好了,理论讲完了,咱们来个综合例子。假设我们要做一个温控风扇:温度超过30度开启,低于25度关闭。
#include <stdio.h>
int main() {
float temperature;
int fan_status = 0; // 0:关闭, 1:开启
while (1) {
// 读取温度(这里用模拟值)
temperature = 28.5;
// 控制逻辑
if (temperature > 30.0) {
fan_status = 1;
} else if (temperature < 25.0) {
fan_status = 0;
}
// 25~30度之间保持原状态,避免频繁开关
// 输出状态
if (fan_status) {
printf("风扇开启\n");
} else {
printf("风扇关闭\n");
}
// 延时1秒
// delay(1000);
}
return 0;
}
这个例子用到了数据类型(float、int)、运算符(比较)、流程控制(if/else)、循环(while)。你看,基础知识点串起来就能解决实际问题。
好了,这一章就到这里。下一章咱们继续回顾C语言的函数、数组和指针——这些才是嵌入式开发的「硬菜」。记得动手敲代码,光看是学不会的。