下面的非常简单的代码是否容易受到未定义行为的影响,因为整数由于操作而溢出?
static volatile LONG x = LONG_MAX;
InterlockedIncrement(&x);
根据标准,带符号整数溢出是未定义的行为。但是,这里我们不符合标准,因为我们正在调用编译器的内在函数,该函数内联到某些程序集。另外,x
的值不会在任何地方使用(该功能仅用作内存屏障)。
answer to a similar question提示这不是UB。
答案 0 :(得分:2)
我声称这里既没有UB,也没有语言标准(该标准不涵盖此功能/本征)或实现,都没有UB,并且有一个简单的过渡。
这是我的理由...
InterlockedIncrement()
从概念上讲非常简单,并且如果有特殊情况,将很难错过并且无法记录下来。而且文档在15年以上的时间里都没有提到任何特殊情况。
您将如何实施它?
如果您使用的是80486或更高版本,则最自然的实现方式是使用带有XADD
前缀的LOCK
指令,该指令原子地向内存变量添加一个值。该指令本身不会产生任何溢出异常,但是它会像常规加法指令EFLAGS
一样修改ADD
寄存器,因此可以检测到溢出并对其进行处理。具体来说,您可以抛出INTO
指令以将溢出条件变为异常。或者,您可以使用条件跳转指令JO
来跳转溢出处理程序。
如果您使用的是80386或更高版本,则还可以使用XCHG
指令(此指令隐含LOCK
),以创建一个循环,尝试自动更新内存变量(这是可以实现InterlockedExchange()和InterlockedCompareExchange()的方式,从80486开始,还有一条更方便的(为此目的)CMPXCHG
指令)。在这种情况下,您需要像往常一样使用ADD
指令或INC
指令执行寄存器递增,并且可以选择检测任何溢出条件(在EFLAGS.OF
中),并且如前所述处理它。
现在,您是否要将INTO
或JO
放入InterlockedIncrement()
的所有实例中?可能不是,默认情况下绝对不是。人们喜欢小型而快速的原子操作。
这是“即时” UB。那“爬行的” UB又如何呢? 如果您有这样的C代码:
int a = INT_MAX;
if (a + 1 < a)
puts("Overflow!");
如今您可能会获得nothing printed。
现代编译器知道a + 1
不会合法地(!)溢出,因此if语句中的条件可以被视为false,而与a
的值无关。
您可以对InterlockedIncrement()
进行类似的优化吗?
好吧,假设变量为volatile
并且确实可以随时在不同的线程中更改,则编译器可能不会假设两次内存读取都使a
保持不变(您可能会写a + 1 < a
或类似的多个语句,并且每个a
如果是易失的,都需要获取。
尝试进行优化也很奇怪。