有符号整数溢出,内在函数和未定义的行为

问题描述 投票:0回答:1

以下非常简单的代码是否容易受到未定义的行为的影响,因为整数因操作而溢出?

static volatile LONG x = LONG_MAX;

InterlockedIncrement(&x);

根据标准,有符号整数溢出是未定义的行为。但是,这里我们不符合标准,因为我们正在调用编译器的内部函数,它内联到某个程序集。此外,x的值不在任何地方使用(该函数仅用作内存屏障)。

answer to a similar question表示这不是UB。

c++ windows visual-studio undefined-behavior intrinsics
1个回答
2
投票

我声称这里没有UB,既没有语言标准(标准也没有涵盖这个功能/内在),也没有实现,并且有一个简单的翻转。

这是我的推理......

InterlockedIncrement()在概念上非常简单,如果它有一个特殊情况,很难错过它并且不能记录它。并且文档在这里已经有15年以上没有提到任何特殊情况了。

你怎么会实现它?

如果您使用的是80486或更高版本,则最自然的实现使用带有XADD前缀的LOCK指令,该前缀以原子方式将值添加到内存变量中。该指令本身不会产生任何溢出异常,但它会像常规加法指令EFLAGS那样修改ADD寄存器,因此可以检测溢出并对其进行操作。具体来说,您可以抛出INTO指令将溢出条件转换为异常。或者你可以使用条件跳转指令JO来跳转溢出处理程序。

如果您使用的是80386或更高版本,您还可以使用XCHG指令(LOCK隐含在此指令中),以创建一个尝试以原子方式更新内存变量的循环(这是InterlockedExchange()和InterlockedCompareExchange(如何) )可以实现,自80486以来,还有一个更方便的(为此目的)CMPXCHG指令。在这种情况下,您需要像往常一样使用ADD指令或INC指令执行寄存器增量,并且您可以选择检测任何溢出条件(在EFLAGS.OF中)并按前面所述处理它。

现在,你想把INTOJO扔进所有的InterlockedIncrement()吗?可能不是,绝对不是默认的。人们喜欢他们的原子操作小而快。

这是“直接”的UB。那个“爬行”的UB怎么样?如果您有这样的C代码:

  int a = INT_MAX;
  if (a + 1 < a)
    puts("Overflow!");

你现在可能会得到nothing printed。现代编译器知道a + 1不能合法地(!)溢出,所以if语句中的条件可以被视为假,而不管a的值。

你能和InterlockedIncrement()进行类似的优化吗?

好吧,鉴于变量是volatile并且确实可以在任何时刻在不同的线程中进行更改,编译器可能不会假设a来自它的两个内存读取(您可能会编写a + 1 < a或类似的多个语句,并且每个a都需要如果它是不稳定的,则被取出)。

尝试进行优化也是一个奇怪的背景。

© www.soinside.com 2019 - 2024. All rights reserved.