4、C语言基础回顾(上):数据类型、变量与常量、运算符与表达式、输入输出函数

各位同学,咱们今天开始正式进入C语言的基础回顾。

说实话,我见过不少刚入行的嵌入式工程师,上来就啃寄存器、啃中断,结果连个变量类型都搞混,最后调试到崩溃。我个人习惯是,先把地基打牢。你想想看,轨道车门控制器的代码,一个字节的偏差都可能导致开关门时序错乱,这可不是闹着玩的。

好,咱们开始。

4.1 数据类型:你到底占几个字节?

嵌入式C语言里,数据类型直接决定了你操作的内存大小。我当年在做一个车门状态采集项目时,就因为用了 int 来存一个开关信号,结果在不同编译器下占的字节数不一样,导致数据错位。嗯,这里要注意。

标准C的数据类型,咱们重点掌握这几个:

  • char:1字节,-128 ~ 127 或 0 ~ 255(看是否带符号)。我习惯用它来存小范围的状态值,比如车门开/关标志。
  • short:2字节,-32768 ~ 32767。适合存计数器。
  • int:在8位单片机上通常是2字节,在32位ARM上是4字节。所以,千万别写死长度
  • long:4字节或8字节,看平台。
  • float/double:浮点数,嵌入式里尽量少用,因为运算慢。我一般只在做故障诊断的阈值计算时才用。

重要提醒: 在嵌入式开发中,强烈建议使用 stdint.h 里定义的类型,比如 uint8_tuint16_tuint32_t。这样代码跨平台时,字节长度是确定的,不会出幺蛾子。

我的小技巧: 定义车门状态时,用 uint8_t door_status; 而不是 int door_status;。省内存,而且一看就知道是8位的状态量。

4.2 变量与常量:你的数据是死的还是活的?

变量,说白了就是内存里的一块空间,你可以往里面写数据,也可以读出来。常量,就是写死了的值,程序运行期间不能改。

定义变量时,我建议你养成一个好习惯:先声明,再使用。而且变量名要见名知意。我曾经接手过一个项目,变量名叫 abc,看得我头皮发麻。

// 好的命名
uint8_t door_open_flag;      // 车门打开标志
uint16_t motor_speed_rpm;    // 电机转速

// 差的命名
int a;                       // 谁知道这是啥?
int b;                       // 猜去吧

常量有两种定义方式:

  • #define:预处理阶段替换,不占内存。比如 #define DOOR_OPEN 1
  • const:编译阶段处理,占内存,但类型安全。比如 const uint8_t DOOR_OPEN = 1;

我个人习惯,简单的数值宏用 #define,需要类型检查的用 const。你想想看,如果定义了一个 const float 的阈值,编译器会帮你检查类型,不容易出错。

避坑指南: 我曾经在定义车门超时时间时,用了 #define TIMEOUT 5000,结果在另一个文件里不小心重定义了,导致编译报错。后来我改用 const uint16_t TIMEOUT = 5000;,就再没出过这种问题。

4.3 运算符与表达式:让数据动起来

运算符就是C语言里的「动作指令」。咱们嵌入式开发中,最常用的几类:

算术运算符

+-*/%。注意,整数除法会丢掉小数部分。比如 5 / 2 = 2,不是2.5。我刚开始学的时候,就在这里栽过跟头。

关系运算符

><==!=。用来比较大小。注意 === 的区别,写错一个,程序逻辑全乱。我曾经在判断车门是否关紧时,写成了 if (door_status = CLOSED),结果永远为真,车门一直显示关闭状态。嗯,这个坑我记忆犹新。

逻辑运算符

&&(与)、||(或)、!(非)。用来组合条件。比如:

if (door_closed && motor_stopped) {
    // 允许开门
}

位运算符

这是嵌入式开发的精髓。&|^~<<>>。直接操作寄存器的每一位。

实战案例: 假设你要设置某个寄存器的第3位为1,其他位不变:

REG |= (1 << 3);   // 置位第3位

要清除第3位:

REG &= ~(1 << 3);  // 清零第3位

你想想看,如果没有位运算,你要怎么操作?用加减法?那效率低得可怕。

4.4 输入输出函数:和硬件打个招呼

在嵌入式里,输入输出函数通常不是标准库里的 printfscanf,而是直接操作寄存器或调用硬件抽象层接口。但为了学习,咱们先用标准库的来理解概念。

printf:输出到控制台

格式化输出。比如:

uint8_t door_count = 3;
printf("当前车门数量: %d\n", door_count);

常用的格式符:%d(整数)、%f(浮点数)、%x(十六进制)、%c(字符)。

我的习惯: 调试时,我经常用 printf 打印关键变量的值。但正式产品里,我会把它关掉,或者用串口输出到调试工具。因为 printf 太占资源了。

scanf:从控制台输入

这个在嵌入式里用得少,因为很少有键盘输入。但理解它的原理很重要:它从标准输入读取数据,按格式解析。

int speed;
scanf("%d", &speed);  // 注意 & 符号

这里有个容易犯的错:scanf 需要变量的地址,所以要用 &。我见过不少新手忘了加 &,结果程序直接崩溃。

避坑指南: 在真正的嵌入式项目中,输入输出通常是通过串口中断或DMA来实现的。标准库的 scanf 是阻塞的,会卡死整个系统。所以,千万别在产品代码里直接用 scanf。我当年在调试一个车门控制器时,就因为用了 scanf 等待输入,结果车门开关指令被阻塞,差点出事故。

好了,这一章的内容就到这里。数据类型、变量常量、运算符、输入输出,这些都是C语言的骨架。你把这些搞明白了,后面写代码就会顺手很多。

下一章,咱们继续回顾C语言的控制流和数组。到时候我会结合轨道车门的实际场景,给你讲讲怎么用 if-elseswitch 来控制车门状态机。


公众号:蓝海资料掘金营,微信deep3321