有符号整数溢出、内部函数和未定义的行为
Signed integer overflow, intrinsics, and undefined behaviour
下面的非常简单的代码是否容易受到未定义行为的影响,因为整数由于操作而溢出?
static volatile LONG x = LONG_MAX;
InterlockedIncrement(&x);
根据该标准,有符号整数溢出是未定义的行为。但是,这里我们超出了标准,因为我们调用编译器的内在函数,该函数内联到某个汇编。此外,x
的值不会在任何地方使用(该函数仅用作内存屏障)。
对类似问题的回答表明这不是 UB。
我声称这里没有UB,无论是根据语言标准(该标准不包括此功能/内部函数)还是根据实现,并且有一个简单的滚动。
这是我的推理...
InterlockedIncrement()
在概念上非常简单,如果它有一个特殊情况,就很难错过它并且无法记录它。并且文档在这里没有提到任何特殊情况 大约 15+ 年。
无论如何,您将如何实现它?
如果您使用的是 80486 或更高版本,则最自然的实现使用带有LOCK
前缀的XADD
指令,该前缀以原子方式向内存变量添加值。指令本身不会生成任何溢出异常,但它确实会像常规加法指令一样修改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!");
你现在可能什么也印不出来。 现代编译器知道a + 1
不能合法地(!)溢出,因此无论a
的值如何,if语句中的条件都可以被视为false。
你能对InterlockedIncrement()
进行类似的优化吗?
好吧,鉴于变量是volatile
的,并且确实可以随时在不同的线程中更改,编译器可能不会假设它的两次内存读取a
不变(您可能会编写a + 1 < a
或类似于多个语句,并且如果它是易失性的,则需要获取每个a
)。
尝试进行优化也将是一个奇怪的上下文。
- 从python调用openMP共享库时,未定义opnMP函数
- CMake 不链接 C 和 C++ 静态库(未定义对函数的引用)
- 类中未定义的函数
- 未定义原型函数?
- 模拟 __name__ = __main__ 在 c++ 中会导致错误"未定义类似函数的宏"
- C++11 未定义对函数的引用
- 获取错误:未定义对函数 Android NDK 的引用
- 查找已声明但未定义的函数
- 当所有信息都在头文件中时,未定义对函数的引用
- 未定义对"函数"的引用 - 链接器问题?
- SDL2要么丢失OpenGL上下文或未定义的函数
- 在 C++ 中未定义对函数的引用
- 调用未定义的函数"save"
- C++未定义对函数的引用
- g++链接错误:使用#define编写函数名称时,未定义对函数的引用
- cmake链接本地文件期间未定义的函数
- C++FatFs未定义对函数的引用
- opencv 中未定义的函数
- C++ 将参数数量未定义的函数传递给类
- 使用指向未定义成员函数的指针时的未定义引用