经验:细节决定成败...令人抓狂的逻辑运算符&&和||

问题发现

今天在实现STM8S的UART收发时,出现这样一种情况,如图1:
异常图

图1

代码功能是通过UART接收一个字符,字符自增一,然后输出

所以按照逻辑,代码执行顺序应该是:

PC端通过Tx发送字符 -> STM8S端通过Rx接收字符,字符自增一 -> STM8S端通过Tx发送字符 -> PC端通过Rx接收字符

所以图1中PC端还没发送字符,STM8S端就已经发送字符过来了,这显然不符合我们的设计思路





解决思路

毫无疑问,从主函数main()入手,主函数的主要代码部分为下图:
主函数主要代码代码

图2

可见,代码实现符合我们的收发逻辑,现在目光转到函数UART_RX()的定义处
UART_RX()定义

图3

现在我们看到while(!(UART1_SR && (1<<5)));语句,该语句功能用文字可描述为: 检查UART1_SR寄存器的bit 5是否为1,如为1则跳出循环,0则继续循环

此时罪魁祸首出现了,即&&逻辑与运算符,将&&改为&(在 涉及知识点 部分有详细关于&&&的异同),然后重新编译执行,结果如下:
正常运行

图4

可见PC端的收发数据表现正常





涉及知识点

有些啰嗦,但顺便复习一下

while (表达式的真假(值))
{
代码块
}

while语句中括号 " () "里的应摆放一个表达式,而C的表达式具有以下属性:

  • 运算符
  • 运算对象
  • 表达式的值

ATTENTION

  1. 最简单的表达式可以是一个单独的运算对象,即常量/变量

  2. 采用一般运算符的表达式,会得到“

  3. 采用 逻辑/关系 运算符的表达式,也会得到“ ”,但是这个“ ”通常被称为“真(true)/假(false) ” 。这个真假值逻辑/关系 表达式的运算结果决定。非0数都为“ ”,有且仅有0表示“ ”(由1. 可知,一个非0的常量/变量,也可以表示真;非0数都为“ ”同时常量0也表示“

对于关系运算符有以下例子:
例1:

while (1 == 1)// 关系表达式1 == 1,有返回值1,1为非0数,while语句判定为真,进入循环体      
{ 
 // 代码块
}

例2:

while (1 > 2) // 关系表达式1 > 2,有返回值0,while语句判定为假,不进入循环体,执行循环体后的代码      
{ 
	// 代码块
 }

对于逻辑运算符有以下例子:
例3:

int a = 1, b = 1;
while (a & b)
// 1. 逻辑表达式a & b,有返回值1,1为非0数,表达式值为真
// 2. while语句判定为真,执行循环体语句      
{ 
 // 代码块
}

例4:

int a = 2, b = 1, c = 0;
while ((a > b) && (b > c))
// 1. 关系表达式a > b成立,有返回值1,1为非0数,表达式值为真
// 2. 关系表达式b > c成立,有返回值1,1为非0数,表达式值为真
// 3. 即有while(真 && 真)     
// 4. while语句判定为真,执行循环体语句
{ 
 // 代码块
}

例5:

int a = 2, b = 1, c = 0;
while ((a < b) && (b > c))
// 1. 关系表达式a < b不成立,有返回值0,表达式值为假
// 2. &&运算符不执行右侧关系表达式(b > c)的运算,直接两个表达式的结果为假;
//    因为与运算中只要有一端的值为假,那么整个表达式的值为假
// 3. while表达式判定为假,执行循环体之后的语句
{ 
 // 代码块
}

ATTENTION
从结合例3、例4、例5比对我们有以下结论:

对于&&运算符在例4、例5的比较,有

  • &&运算符左侧的运算对象为false时,则不会判断&&右侧的运算对象 ,执行后面的语句

对于&运算符和&&运算符在例3、例4的比较,有

  • &直接取两端的表达式进行与运算,返回一个值,根据该值运算的值判断真假
  • &运算符在已知左侧运算对象结果为false的情况,仍然会计算其右侧的运算对象,因为需要知道 &两端的真值来进行与运算
  • &&运算符参与的逻辑表达式,则要求两端表达式的值均为真即表示 &&表达式结果为真,不拿两端的表达式做与运算
  • &&左侧表达式真值为假,直接判定为假,执行后续语句

这样就能解释为什么while(!(UART1_SR & (1<<5))); 能正常收发数据,分析如下:
UART1_SR有默认值0xC0 = 0b1100 0000,位移表达式有值 (1<<5) = 0b0010 0000 ,两表达式与运算结果为false,!(false)为真,while一直执行空循环,则需等条件满足才能跳出循环

while(!(UART1_SR && (1<<5))); 却会导致PC端还没发送数据,STM8S端就已经有数据发送过来了,分析如下:
UART1_SR有默认值0xC0 = 0b1100 0000 恒为真,位移表达式有值 (1<<5) = 0b0010 0000 恒为真,由上述例子结论

&&则要求两端表达式的值均为真即可,不拿两端的表达式做与运算

可知,while(!(UART1_SR && (1<<5)));while(!(true)),表示恒为假,没有执行执行空循环的循环体,直接职执行后面的语句


关于&&&的使用时机

  • 利用&&的机制我们可以把它用在判断语句中,这样效率更高,既然知道&&前表达式结果为假,那么就不需要计算后面的表达式了
  • 对于&则老老实实用于表达式的与运算中,求两端表达式值的与值

关于 ||| 运算符

  • 或运算符和与运算符一直,当为||时,左侧表达式为真,则直接返回真,不去运算右侧表达式的值
  • |时则两侧都作运算,因为需要两端的值来做或运算




补充

while(!(UART1_SR && (1<<5))); 时,程序详细发生了什么
在这里插入图片描述
涉及知识点 我们得知了&&的运行机制,不妨分析此时程序发生了什么

  1. 首先,查看参考手册,可知UART1_SR的默认值为0xC0
    UART1_SR的默认值
  2. 因为UART1_SR为非0值,所以恒为真;(1<<5) 表达式的值也为非0,所以也恒为真;此时回到表达式UART1_SR && (1<<5),由于两侧表达式结果恒为真, 根据&&的特性,UART1_SR && (1<<5) 表达式的返回值恒为真
  3. 不妨将 UART1_SR && (1<<5) 设为true,则表达式!(UART1_SR && (1<<5))等价于!(true)!(true)又等价于false
  4. false带回语句while(false); false表示值为0,while语句跳过不执行循环体,执行循环体下一条语句return UART1_DR; 十分关键,这表示了,STM8S并没有收到任何来自PC端的输入,但是通过软件将UART1_DR的原始值输出了

所以UART的收发逻辑流程从

PC端通过Tx发送字符 -> STM8S端通过Rx接收字符,字符自增一 -> STM8S端通过Tx发送字符 -> PC端通过Rx接收字符

变成了

STM8S端通过Tx以字符形式发送存放在UART1_DR的默认值 -> PC端通过Rx接收字符

而且因为不需要PC端输入,下面这个流程会一直持续!!!! 所以就解释了为什么会出现PC端Rx和Tx数据不对等的情况
异常





总结

按理说,这是一个很简单的问题,但因为没有注意细节,导致了bug的出现,花费大量时间debug才发现是一个逻辑符号处理逻辑的问题,所以说基础是一切根本,其他技术都是基础的延伸

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值