关于C语言无符号变量算术运算时由于类型提升造成的BUG

**

关于C语言算术运算时类型提升的坑

**

起因

在stm32写延时函数时,使用16位定时器TIM6做延时函数,代码如下:.

// 延时函数
void DelayUs(uint16_t nUs)
{
	uint16_t tickStart = TIM6->CNT;
	/* 延时等待 */
	while (TIM6->CNT - tickStart < nUs);
}

原理就是不停的读TIM6定时器的计数值和初始值作差和要延时的值作比较。
但是实际测试时,发现程序会死在while循环里出不来,怀疑是优化问题或者是无符号变量相减问题。

问题分析

由于看不懂ARM的汇编指令,开始没有对汇编代码仔细分析。
起初怀疑是代码被编译器优化了。所以做了如下测试:

  1. tickStart改用volatile修饰,但是发现仍不起作用;
  2. 尝试将优化等级调整为-O0,仍不行;
  3. while循环增加循环主体,在循环里翻转IO,发现一直翻转,确实跳不出循环;
  4. 尝试利用中间变量,测试功能恢复正常,代码修改如下;
// 延时函数
void DelayUs(uint16_t nUs)
{
   uint16_t tickStart = TIM6->CNT;

   uint16_t value=TIM6->CNT - tickStart;
   /* 延时等待 */
   while (value < nUs){value=TIM6->CNT - tickStart;};

}
  1. 既然中间变量可以,那么考虑可能为无符号类型相关,直接强制类型转换测试功能正常,代码如下
// 延时函数
void DelayUs(uint16_t nUs)
{
  uint16_t tickStart = TIM6->CNT;

  /* 延时等待 */
  while ((uint16_t)(TIM6->CNT - tickStart) < nUs);

}

类似代码验证


void test(void)
{
  uint8_t a=10;
  uint8_t b=20;
  if(a-b>1)
  	printf("1\n");
  else
  	printf("2\n");

}

在keil和DEV C++中验证,输出均为2
在deepseek和百度文心一言中测试,输出均为1
明显AI是错的

原因分析

直接将正确结果告诉AI,AI分析结果如下:

关键点:整数提升和类型转换
在C语言中,当进行算术运算时,较小的整数类型(如 uint8_t)会被提升为 int 类型(除非 int 无法表示所有值,这种情况下会提升为 unsigned int)。这是“整数提升”(integer promotion)的规则。
表达式 a - b 的实际计算
整数提升:a 和 b 都是 uint8_t 类型(8位无符号整数)。
在进行 a - b 时,它们会被提升为 int 类型(假设 int 是32位,这是常见的情况)。
因此,a 提升为 10(int),b 提升为 20(int)。
a - b 计算为 10 - 20 = -10(int 类型)。
比较 -10 > 1:-10 是负数,显然不大于 1,因此条件为假。
因此,else 分支被执行,输出 2

也就是说,编译器将比变量宽度小于Int类型的数据均提升到Int类型计算,无符号变成了有符号的数。

验证

  uint16_t a=0x0ff0;
  uint16_t b=0x0fff;
  if(a-b>1)
  	printf("1\r\n");
  else
  	printf("2\r\n");

结果仍为2
说明Uint16_t也被类型提升成了int类型的有符号数了。

  uint32_t a=0x0ff0;
  uint32_t b=0x0fff;
  if(a-b>1)
  	printf("1\r\n");
  else
  	printf("2\r\n");

将类型改为uint32_t,输出结果为1
说明类型没有被提升。

到此,验证正确

汇编源码分析

这是uint16_t的汇编代码在这里插入图片描述

这是uitnt32_t的汇编代码在这里插入图片描述
比较两者代码,发现只有一处不同,就是0x08000f04处
uint16使用的是BLE指令,uint32_t处使用的是BLS指令

求助AI,介绍如下:
在ARM汇编中,BLE和BLS是两种不同的条件分支指令,它们的主要区别体现在应用场景、比较类型及条件判断依据上,以下是具体分析:

应用场景
BLE:通常用于实现有符号数的比较和分支,在需要判断有符号数是否小于或等于时使用。
BLS:通常用于实现无符号数的比较和分支,在需要判断无符号数是否小于或等于时使用。
比较类型
BLE:涉及有符号数的比较,会考虑数值的正负和大小。
BLS:涉及无符号数的比较,只考虑数值的大小,不考虑正负。
条件判断依据
BLE:条件分支指令,当上一次运算结果为有符号数小于等于时分支。
BLS:条件分支指令,当上一次运算结果为无符号数小于等于时分支。

确实如之前分析的那样:
uint16_t/uint8_t相减是按有符号处理的,uint32_t是按无符号处理的
使用时千万小心。

总结

类型提升会导致数据类型发生变化,无符号的数会变成有符号的数。
该问题主要是出现在无符号数据相减时,常见于时间戳判断上,时间戳溢出导致该坑。
解决办法:

  1. 相减后的结果强制类型转换
  2. 使用更大类型的数据,保证编译器不会提升数据类型
  3. 直接代码中判断是否溢出(稍微啰嗦一点)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值