我在网络协议中使用uint16_t
作为序列计数器。这个计数器通常会按预期包装。当接收方获取数据包时,它会根据最近收到的数据来检查此计数器,以查看它是新数据包还是无序数据包。
比较序列号时需要考虑环绕声。因此,如果最后一个序列号是0x4000
,那么从0x4001
到0xBFFF
的序列号更新,序列号从0xC000
到0xFFFF
从0x0000
到0x3FFF
的范围更小。
我目前的做法如下:
uint16_t last;
uint16_t current;
...
// read in values for last and current
...
if ((int16_t)(current - last) > 0) {
printf("current is newer\n");
} else {
printf("current is older (or same)\n");
}
通过减去两者并将结果视为int16_t
,我可以很容易地看出哪个更大,哪个更多。因此,例如,如果当前序列号至少比最后一个序列号少5,即((int16_t)(current - last) < -5)
,我可以假设这不是由于正常的数据包重新排序和丢弃数据包。
我意识到签名的回绕是未定义的,但是在这种情况下,为了进行比较,我将无符号值视为已签名。这是否会调用未定义的行为,如果是这样,那么进行此类比较的更好方法是什么?
答案 0 :(得分:6)
超出范围转换的行为是实现定义的。
为什么不完全避免这个问题并写下:
if ( current != last && current - last < 0x8000 )
printf("current is newer\n");
else
printf("current is older (or same)\n");
注意:此答案仅适用于涉及uint16_t
的特定问题。对于其他类型,将需要不同的代码。
答案 1 :(得分:0)
您可以将uint16_t
转换为int32_t
并进行减法。
if (((int32_t) current - (int32_t) last) > 0) {
printf("current is newer\n");
} else {
printf("current is older (or same)\n");
}
注意:当转换为int16_t
时,uint16_t
s大于32767将显示为负数(在有符号整数中,最高有效位设置为1表示负数,在无符号类型中它只是一个常规位)。
答案 2 :(得分:-1)
减法将使用提升类型进行,除非int
为16位,在这种情况下行为是实现定义的。
为了安全起见,您可以使用三元计算绝对差值:
current > last ? current - last : last - current