运算符就是让数据之间发生关系的符号。这一篇讲三类:逻辑运算符(判断真假)、条件运算符(一句话二选一)、位运算符(直接摆弄二进制位)。其中位运算在上位机里特别常用,比如读 PLC 一个字节里的开关状态。

本篇目标:会用 &&、||、! 做判断,会用 ?: 做二选一,会用 &、|、<<、>> 做位操作,能读懂「读一个字节里的 8 个开关」这种经典代码。


零、先分清三类运算符

类别 符号 作用 结果类型
逻辑运算符 && ` !`
条件运算符 ?: 二选一取值 看情况
位运算符 & ` ^ ~` << >> 直接操作二进制位
逻辑运算符条件运算符 是「判断」,位运算符 是「摆弄二进制」。下面一个个来。

一、逻辑运算符:判断真假

逻辑运算符的答案只有两个:真(true)或假(false)。它用来回答「是不是、有没有、满不满足条件」这类问题。

三个符号先记牢:

  • 与(&&):两边都为真,结果才为真。像「两个条件都要满足」。
  • 或(||):两边只要有一个为真,结果就为真。像「两个条件满足一个就行」。
  • 非(!):取反,真变假,假变真。像「反过来」。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;

class Program
{
static void Main()
{
double temp = 85.0; // 当前温度
bool pressureOk = true; // 压力是否正常

bool overHeat = temp > 80.0; // 温度是否超标
bool alarm = overHeat && !pressureOk; // 超标 且 压力不正常,才报警
bool warn = overHeat || !pressureOk; // 超标 或 压力不正常,就预警

Console.WriteLine(overHeat); // True
Console.WriteLine(alarm); // False
Console.WriteLine(warn); // True
}
}

上面代码里 temp > 80.0 这种比较,结果就是 bool 类型。再用 &&、||、! 把它们组合起来,就得到更复杂的判断。

用真值表记清楚

假设 A 和 B 是两个 bool 值,组合结果如下:

ABA 与 BA 或 B非 A

看「与」这一列:只有两个都真,结果才真。「或」这一列:只要有一个真,结果就真。

逻辑运算符两侧通常是比较式,比如 temp > 80,而不是直接写数字。写 80 && 100 是没意义的。


二、条件运算符:一句话二选一

条件运算符也叫三元运算符,符号是问号和冒号,格式一句话:

条件 ? 值甲 : 值乙

意思是:条件为真就取值甲,条件为假就取值乙。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;

class Program
{
static void Main()
{
double temp = 85.0;

// 温度超标就显示「过高」,否则显示「正常」
string state = temp > 80.0 ? "温度过高" : "温度正常";

Console.WriteLine(state); // 温度过高
}
}
上位机里特别常用

上位机界面上经常要根据数值显示文字,比如:

1
2
3
4
int alarmCode = 0;   // 0 表示无报警,其他表示有报警

string show = alarmCode == 0 ? "运行正常" : "有报警";
Console.WriteLine(show); // 运行正常

以前要写 if else 好几行,现在一行就搞定,特别简洁。

?: 读起来就是:问号是「是不是?」,冒号是「不是的话就」。条件成立走左边,不成立走右边。


三、位运算符:直接摆弄二进制

这是本片的重头戏。位运算不关心数字是几,而是直接操作它底层的二进制 0 和 1。上位机读 PLC、Modbus 的开关状态时,几乎离不开它。

先补一点点二进制基础。一个整数在电脑里是一串 0 和 1,比如数字 5 的二进制是:

1
0000 0101

从右边数,最右边是第 0 位,往左依次是第 1 位、第 2 位……每一位只有 0 或 1 两种状态。5 的二进制里,第 0 位和第 2 位是 1,其余是 0。

位运算符有六个,但新手先掌握四个就够:

  • 与(&):两位都是 1,结果才是 1。
  • 或(|):两位只要有一个 1,结果就是 1。
  • 左移(<<):整体往左挪,空位补 0,相当于乘 2。
  • 右移(>>):整体往右挪,相当于除以 2。

3.1 用「与」读某一位

与运算(&)最常见的用法是:读一个整数里某一位是 0 还是 1。上位机里,一个字节的 8 个位,常常就代表 8 个开关的状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;

class Program
{
static void Main()
{
byte state = 5; // 二进制 0000 0101,第0位和第2位是 1

bool sw0 = (state & 1) != 0; // 第0位:用 1 去与
bool sw1 = (state & 2) != 0; // 第1位:用 2 去与
bool sw2 = (state & 4) != 0; // 第2位:用 4 去与
bool sw3 = (state & 8) != 0; // 第3位:用 8 去与

Console.WriteLine(sw0); // True
Console.WriteLine(sw1); // False
Console.WriteLine(sw2); // True
Console.WriteLine(sw3); // False
}
}
为什么 1、2、4、8 能对应每一位?

因为这几个数正好是 2 的 0、1、2、3 次方,二进制里只有一位是 1,其余全是 0:

数字二进制
10000 0001
20000 0010
40000 0100
80000 1000

用它们去「与」state,等于只把某一位单独抠出来看。这一位是 1,结果就不是 0,开关就是开;是 0,结果就是 0,开关就是关。

3.2 用「或」打开某一位

或运算(|)的常见用法是:把某一位设置成 1(打开开关)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;

class Program
{
static void Main()
{
byte state = 0; // 全部关闭,二进制 0000 0000

state = state | 4; // 打开第2位,state 变成 4
state = state | 1; // 打开第0位,state 变成 5

Console.WriteLine(state); // 5
}
}

「或」能把某位打开,「与」能读某位是开是关。一开一读,就是上位机控制开关量的核心手法。

3.3 用「左移」「右移」快速乘除

左移(<<)和 右移(>>)能快速乘 2 或除 2,也常用来生成位掩码。
1
2
3
4
5
6
7
8
9
10
11
12
13
using System;

class Program
{
static void Main()
{
int a = 1 << 3; // 1 左移 3 位,等于 1 * 2 的 3 次方 = 8
int b = 16 >> 2; // 16 右移 2 位,等于 16 除以 4 = 4

Console.WriteLine(a); // 8
Console.WriteLine(b); // 4
}
}

1 << 3 这个写法在上位机里特别常见,它生成一个「第 3 位是 1」的掩码,等价于写数字 8,但比 8 更直观地表达了「第 3 位」这个意思。


四、逻辑 和 位运算 的区别

新手最容易把 && 和 &、|| 和 | 搞混。它们长得像,但完全是两回事。

符号 名字 结果类型 用途
&& 逻辑与 bool 判断真假,两个条件都满足
& 位与 整数 操作二进制位
` ` 逻辑或
` ` 位或 整数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;

class Program
{
static void Main()
{
// 逻辑与:结果是 bool
bool r1 = (5 > 3) && (2 > 1); // True,两边都真
Console.WriteLine(r1);

// 位与:结果是整数,5 与 3 按位相与
int r2 = 5 & 3; // 二进制 101 与 011 = 001 = 1
Console.WriteLine(r2); // 1
}
}

一句话记死:判断真假用 &&、||,摆弄二进制用 &、|。用错了要么报错,要么结果完全不对。


五、上位机实战:读一个字节的 8 个开关

综合前面学的,看一个最贴近真实场景的例子:PLC 通过 Modbus 传来一个字节,这 8 个位分别代表 8 个开关(比如 8 个阀门)的开关状态,我们把它一个个读出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;

class Program
{
static void Main()
{
// 假设 PLC 传来的一个字节是 170
// 二进制 1010 1010,第1、3、5、7 位是 1
byte state = 170;

// 用左移生成每位对应的掩码,再与运算读出
bool valve0 = (state & (1 << 0)) != 0;
bool valve1 = (state & (1 << 1)) != 0;
bool valve2 = (state & (1 << 2)) != 0;
bool valve3 = (state & (1 << 3)) != 0;

Console.WriteLine("阀门0:" + (valve0 ? "开" : "关"));
Console.WriteLine("阀门1:" + (valve1 ? "开" : "关"));
Console.WriteLine("阀门2:" + (valve2 ? "开" : "关"));
Console.WriteLine("阀门3:" + (valve3 ? "开" : "关"));
}
}
输出结果对照
1
2
3
4
阀门0:关
阀门1:开
阀门2:关
阀门3:开

这里 (1 << 0) 是 1,(1 << 1) 是 2,(1 << 2) 是 4……和前面写数字 1、2、4、8 是一回事,但 1 << 2 更清楚地表达了「第 2 位」。最后再用 ?: 把 true/false 转成「开/关」显示给操作员看,正好把三类运算符全串起来了。


六、小结

这一篇我们学了三类运算符:

  • 逻辑运算符:&&(与)、||(或)、!(非),用来判断真假。
  • 条件运算符:?:,一句话二选一。
  • 位运算符:&(位与)、|(位或)、<<(左移)、>>(右移),直接摆弄二进制。

口诀:

判断真假用 &&、||、!;二选一用 ?:;摆弄二进制用 &、|、<<、>>。

会用 &&、||、! 组合出判断条件

会用 ?: 一行写出二选一

会用 & 读出某一位是 0 还是 1

能分清 && 和 & 的区别


七、能跑起来的小练习

点我看完整练习代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;

class Program
{
static void Main()
{
// 1) 逻辑与条件运算符
double temp = 78.5;
string state = temp > 80.0 ? "温度过高" : "温度正常";
Console.WriteLine(state); // 温度正常

// 2) 位运算读开关
byte sw = 170; // 二进制 1010 1010
bool s1 = (sw & (1 << 1)) != 0;
bool s4 = (sw & (1 << 4)) != 0;
Console.WriteLine(s1); // True
Console.WriteLine(s4); // False

// 3) 位运算打开某一位
byte ctrl = 0;
ctrl = ctrl | (1 << 2); // 打开第2位
Console.WriteLine(ctrl); // 4
}
}