【发布时间】:2015-08-12 13:58:08
【问题描述】:
我在网络协议中使用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),我可以假设这不是由于正常的数据包重新排序并丢弃数据包。
我意识到有符号环绕是未定义的,但是在这种情况下,为了进行比较,我将无符号值视为有符号值。这是否会调用未定义的行为,如果是,那么进行此类比较的更好方法是什么?
【问题讨论】:
-
只要
INT_MAX大于UINT16_MAX(在大多数 32 位或 64 位平台上为 true),就不会有任何未定义的行为,因为算术是在提升的类型上完成的。 -
@Quentin:但是如果
INT_MAX大于INT16_MAX,操作数将被提升。 -
@EOF 我两天前回答了一个关于这个的问题。耻辱:
-
如果您可以节省字节,请使用
uint64_t,不要担心回绕,并希望能够活得足够长,以见证您的协议回绕。通过快速估计,我认为使用 64 位计数器以 1 gB/秒发送 1 kB 数据包需要 500 多年时间。 -
一些通信设备的有效载荷非常有限。例如,许多廉价的 2.4GHz 无线电将数据包限制为总共少于 40 个字节,包括前导码、地址和 CRC。添加额外的 6 个字节的序列号可以将有用的有效负载减少 20% 以上。
标签: c casting undefined-behavior