【STM32】C语言整型提升与HAL库SysTick代码的思考

0 前言

  最近想在标准库中自己实现一个和HAL库一样的超时自动退出的功能,一般适用于一些耗时可能很长的函数。然后在看HAL库的代码时,发现systick定时器部分的代码对于自增的变量没有归零的操作,感觉很奇怪,于是一边上网找资料,一边和朋友讨论,才发现这背后的门道还挺多。

1 问题描述

  在HAL库中延时函数常用HAL_Delay,可以实现毫秒级的延时,定位到其函数实现,可以看到这样的代码:

在这里插入图片描述

再深挖HAL_GetTick函数定义:

在这里插入图片描述

其中,上面那个IncTick的函数是中断服务程序,即1ms执行一次,变量每次+1,下面是获取当前的时刻。那么问题来了,假如这个uwTick变量溢出了怎么办?上面那个表达式(HAL_GetTick() - tickstart) < wait岂不是不成立了?因为有可能出现0x00 - 0xFFFF FFFE的情况,这种情况会正常吗?

2 资料查找与实验验证

  一开始还以为就是个简单的问题,但一查发现网上对此的说法竟然截然相反。一方面,有人觉得这个有问题,需要开发者去修改,即额外定义一个函数对其进行覆盖(原函数定义为weak类型);另一方面,有人觉得这个没有问题,就是这样用的。其中后者的观点也有博客进行了解释,如这篇,但无符号数相减没有负数这样的结论确实太过武断,和我之前的认识有很大出入,于是为了验证真假,决定还是自己做实验验证。

  为了排除编译器和平台的差别,决定在STM32CubeIDE——也是在这里发现的这个问题,上进行测试,代码如下:

uint8_t a = 0x01;
uint8_t b = 0xFE;

if ((a - b) > 0)       // 输出False
	printf("True\n");
else
	printf("False\n");

uint8_t d = 0;
if ((a - b) > d)       // 输出False
	printf("True\n");
else
	printf("False\n");

uint8_t c = a - b;
if (c > 0)             // 输出True
	printf("True\n");
else
	printf("False\n");

if ((uint8_t)(a - b) > 0)  // 输出True
	printf("True\n");
else
	printf("False\n");

于是,我很快得出了结论:“无符号相减如果是赋值给另外一个无符号数,是正值,这显而易见的;但如果是在条件语句中,还是有负值的”。结论与自己原先的想法一致,非常高兴,得意洋洋地给上面那篇文章评论了,同时把这一发现分享给了一位好友,但是朋友貌似并不认可我的发现,坚信库的代码是正确的,毕竟这么大一个公司,而且这么多年了,如果有问题应该会修改才对,于是一通鼓捣之后建议我换个条件再尝试,主要是两个方面:①在数值后面加上U;②改变数据类型,于是又做了一波实验,结果如下,非常Amazing:

在这里插入图片描述

可以发现当数据类型是8位或者16位时,输出的结果是一样的,但是数据类型为32位时,输出结果就发生了改变,全部输出True。

  对此,朋友非常淡定地表示,这个是合理的,这个涉及到运算过程中隐式数据类型变换,恰巧,这个时候之前的那条评论也得到了作者的回复,同样提到运算过程中会对数据进行位扩展,防止溢出,于是再查找了一些资料,简单做个总结。

3 理论总结

  在C语言中,定义一个变量需要使用数据类型,这其实决定了这个变量的存储空间。但在参与运算时,存在一个整型提升的概念。即在表达式计算时,各种整形首先要提升为int类型,如果int类型不足以表示则要提升为unsigned int类型;然后执行表达式的运算。 而且这个提升的过程是隐式的。这是C语言中的一项规定,和使用的平台没有关系。

  实际运算时,数据类型提升的顺序如下图所示:

在这里插入图片描述

参考链接

  以STM32为例,从库函数的代码可以看到,int类型对应的就是32位数据:

在这里插入图片描述

注意,在STM32中,short=2字节,long=int=4字节,long long=8字节,和x86系统是一样的。

  基于以上理论,再来解释以上代码运行的结果,就容易很多了。

uint8_t a = 0x01;
uint8_t b = 0xFE;

if ((a - b) > 0)     // 输出false
	printf("True\n");
else
	printf("False\n")
// 这里的0默认视为int类型,即0x0000 0000s(s表示signed int)
// a在运算时被提升到int,即0x0000 0001s
// b在运算时被提升到int,即0x0000 00FEs
// 则a-b为0x0000 0001s - 0x0000 00FEs = 0xFFFF FF03s 为负数,false

if ((a - b) > 0U)  // 输出True 
	printf("True\n");
else
	printf("False\n");
// 这里的0加上后缀U,默认视为unsigned int类型,即0x0000 0000u(u表示unsigned int)
// a在运算时被提升到int,即0x0000 0001s,但由于出现了更大的unsigned int,
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记录无知岁月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值