第4章 CAPL运算符与表达式
运算符这东西,说白了就是让变量干活的工具。CAPL里的运算符种类不少,但别怕,咱们一个一个捋清楚。我刚开始学的时候也觉得多,后来发现其实就那几类,用熟了自然就记住了。
4.1 算术运算符
算术运算符就是做数学运算的。加减乘除,跟小学数学差不多,但有几个坑要注意。
| 运算符 | 含义 | 示例 |
|---|---|---|
| + | 加法 | a + b |
| - | 减法 | a - b |
| * | 乘法 | a * b |
| / | 除法 | a / b |
| % | 取模(求余数) | a % b |
| ++ | 自增1 | a++ 或 ++a |
| -- | 自减1 | a-- 或 --a |
自增自减运算符,++a 和 a++ 有区别。++a 是先加再用,a++ 是先用再加。举个例子:
int a = 5;
int b = ++a; // a先变成6,然后赋值给b,b=6
int c = a++; // 先把a的值赋给c,c=6,然后a变成7
我个人习惯在循环里用 a++,因为更直观。但在复杂表达式里,我建议你尽量拆开写,别搞得太花哨,容易把自己绕晕。
4.2 关系运算符
关系运算符用来比较两个值的大小或是否相等。结果只有两种:真(1)或假(0)。
| 运算符 | 含义 | 示例 |
|---|---|---|
| == | 等于 | a == b |
| != | 不等于 | a != b |
| > | 大于 | a > b |
| < | 小于 | a < b |
| >= | 大于等于 | a >= b |
| <= | 小于等于 | a <= b |
4.3 逻辑运算符
逻辑运算符用来组合多个条件。你想想看,实际项目中哪有那么简单的条件?经常是「车速大于30且小于80」这种复合条件。
| 运算符 | 含义 | 示例 |
|---|---|---|
| && | 逻辑与(且) | a && b |
| || | 逻辑或(或) | a || b |
| ! | 逻辑非(取反) | !a |
逻辑与有个特点:短路求值。如果第一个条件已经是假,第二个条件就不会执行了。比如:
if (ptr != NULL && ptr->value == 5) {
// 如果ptr是NULL,第二个条件根本不会执行
// 这样就避免了空指针访问
}
这个特性其实很有用。我在写CAN报文解析时经常用,先判断报文是否存在,再判断里面的信号值,安全又高效。
4.4 位运算符
位运算符直接操作二进制位。做汽车电子的人,位运算几乎是家常便饭。因为CAN报文里的信号经常是按位定义的。
| 运算符 | 含义 | 示例 |
|---|---|---|
| & | 按位与 | a & b |
| | | 按位或 | a | b |
| ^ | 按位异或 | a ^ b |
| ~ | 按位取反 | ~a |
| << | 左移 | a << 2 |
| >> | 右移 | a >> 2 |
举个例子,假设你要从CAN报文里提取第3到第5位:
byte rawData = 0xAB; // 二进制: 10101011
// 提取第3-5位(从0开始计数)
byte result = (rawData >> 3) & 0x07;
// 先右移3位,变成 00010101
// 再与0x07(00000111)按位与,得到 00000101 = 5
4.5 赋值运算符
赋值运算符就是把右边的值赋给左边。除了基本的 =,还有复合赋值运算符。
| 运算符 | 含义 | 等价写法 |
|---|---|---|
| = | 赋值 | a = b |
| += | 加后赋值 | a = a + b |
| -= | 减后赋值 | a = a - b |
| *= | 乘后赋值 | a = a * b |
| /= | 除后赋值 | a = a / b |
| %= | 取模后赋值 | a = a % b |
| &= | 按位与后赋值 | a = a & b |
| |= | 按位或后赋值 | a = a | b |
| ^= | 按位异或后赋值 | a = a ^ b |
| <<= | 左移后赋值 | a = a << b |
| >>= | 右移后赋值 | a = a >> b |
复合赋值的好处是代码更简洁。比如计数器累加:
int counter = 0;
counter += 5; // 等价于 counter = counter + 5
我个人习惯在循环里用 += 和 -=,看着清爽。但别滥用,如果逻辑复杂,还是拆开写更清晰。
4.6 运算符优先级
运算符优先级决定了表达式里谁先算谁后算。优先级高的先执行。我直接给你一张表,建议收藏:
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 1(最高) | () [] . -> | 左到右 |
| 2 | ! ~ ++ -- +(正号) -(负号) | 右到左 |
| 3 | * / % | 左到右 |
| 4 | + - | 左到右 |
| 5 | << >> | 左到右 |
| 6 | < <= > >= | 左到右 |
| 7 | == != | 左到右 |
| 8 | &(按位与) | 左到右 |
| 9 | ^(按位异或) | 左到右 |
| 10 | |(按位或) | 左到右 |
| 11 | && | 左到右 |
| 12 | || | 左到右 |
| 13 | ?:(三目运算符) | 右到左 |
| 14 | = += -= 等赋值运算符 | 右到左 |
| 15(最低) | ,(逗号运算符) | 左到右 |
举个例子,看看加了括号和没加括号的区别:
int a = 5;
int b = 3;
int c = 2;
// 不加括号
int result1 = a + b * c; // 先算 b*c=6,再加 a,result1=11
// 加括号
int result2 = (a + b) * c; // 先算 a+b=8,再乘 c,result2=16
嗯,这里要注意:三目运算符 ?: 的优先级很低,比赋值运算符还低。所以:
int x = a > b ? a : b; // 这样写没问题
// 等价于 int x = (a > b ? a : b);
但如果你在复杂表达式里用三目运算符,最好加括号。我见过有人写:
int y = a > b ? a : b + 1; // 实际是 a > b ? a : (b + 1)
// 而不是 (a > b ? a : b) + 1
这种代码容易让人误解。我的建议是:三目运算符尽量用在简单场景,复杂逻辑还是用 if-else 吧,可读性更好。
- 算术运算:注意整数除法截断
- 关系运算:小心 == 和 = 搞混
- 逻辑运算:利用短路求值特性
- 位运算:做信号提取时特别好用
- 赋值运算:复合赋值让代码更简洁
- 优先级:拿不准就加括号,别逞能
运算符这块内容不算难,但细节多。你写代码的时候多留个心眼,慢慢就熟练了。下一章咱们聊控制语句,if、while、for 这些,到时候这些运算符就派上大用场了。