是否通过强制转换为已签名的未定义行为来检测未签名的环绕?
Is detecting unsigned wraparound via cast to signed undefined behavior?
我在网络协议中使用 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
是 16 位,否则减法将使用提升的类型进行,在这种情况下,行为是实现定义的。
为了安全起见,您可以使用三元计算绝对差:
current > last ? current - last : last - current
超出范围的转换行为是实现定义的。
你为什么不完全避免这个问题并写下:
if ( current != last && current - last < 0x8000 )
printf("current is newer\n");
else
printf("current is older (or same)\n");
注:此回答仅适用于涉及uint16_t
的具体问题。对于其他类型,将需要不同的代码。
您可以将 uint16_t
s 转换为 int32_t
s 并进行减法。
if (((int32_t) current - (int32_t) last) > 0) {
printf("current is newer\n");
} else {
printf("current is older (or same)\n");
}
注意:当转换为 int16_t
时,大于 32767 的 uint16_t
s 将显示为负数(在有符号整数中,最高有效位设置为 1 表示负数,在无符号类型中它只是一个常规位)。
我在网络协议中使用 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
是 16 位,否则减法将使用提升的类型进行,在这种情况下,行为是实现定义的。
为了安全起见,您可以使用三元计算绝对差:
current > last ? current - last : last - current
超出范围的转换行为是实现定义的。
你为什么不完全避免这个问题并写下:
if ( current != last && current - last < 0x8000 )
printf("current is newer\n");
else
printf("current is older (or same)\n");
注:此回答仅适用于涉及uint16_t
的具体问题。对于其他类型,将需要不同的代码。
您可以将 uint16_t
s 转换为 int32_t
s 并进行减法。
if (((int32_t) current - (int32_t) last) > 0) {
printf("current is newer\n");
} else {
printf("current is older (or same)\n");
}
注意:当转换为 int16_t
时,大于 32767 的 uint16_t
s 将显示为负数(在有符号整数中,最高有效位设置为 1 表示负数,在无符号类型中它只是一个常规位)。