-fwrapv做什么?

任何人都可以提供一些代码示例,当使用fwrapv vs编译时,它们的行为会有所不同。

它说–fwrapv应该“假设加法,减法和乘法的带符号算术溢出,使用二进制补码表示包装。”

但每当我尝试溢出时,无论有没有fwrapv,结果都是一样的。

想想这个function:

 int f(int i) { return i+1 > i; } 

从数学上讲,对于任何整数ii+1应始终大于i 。 但是,对于32位int ,有一个值使得该语句为false,即2147483647 (即0x7FFFFFFF ,即INT_MAX )。 添加一个到那个数字将导致溢出,根据2的恭维表示,新值将环绕并变为-2147483648 。 因此, i+1>1变为-2147483648>2147483647 ,这是假的。

没有-fwrapv的情况下进行编译时,编译器将假定溢出是“非换行”,并且它将优化该函数以始终返回1 (忽略溢出情况)。

使用-fwrapv编译 ,函数将不会被优化 ,并且它将具有添加1并比较这两个值的逻辑,因为现在溢出是’换行’(即溢出的数字将根据2的补码表示进行换行)。

 for (int i=0; i>=0; i++) printf("%d\n", i); 

使用-fwrapv ,循环将在INT_MAX迭代后终止。 没有,它可以做任何事情,因为当i有值INT_MAX时,通过评估i++无条件地调用未定义的行为。 实际上,优化编译器可能会省略循环条件并产生无限循环。

ISO标准工作组WG14的存在是为了建立所有C编译器必须遵守的约定。 一些编译器可能(并且确实)也实现了扩展。 根据ISO标准C,这些扩展被认为是以下之一:

  • 实现定义 ,意味着编译器开发人员必须做出选择,记录该选择并维护该批次,以便被认为是兼容的C实现。
  • C11 / 3.4.3为未定义的行为建立了一个定义,并提供了一个非常熟悉的例子 ,它远远优于我能写的任何东西:

    1使用不可移植或错误的程序结构或错误数据时的未定义行为行为,本国际标准不对此要求

    2注意可能的未定义行为包括完全忽略不可预测的结果,在转换或程序执行期间以环境特征(有或没有发出诊断消息)的文档方式行为,终止翻译或执行(与发出诊断信息)。

    3示例未定义行为的示例是整数溢出的行为。


还有一个未指明的行为 ,但我会把它作为练习让你在标准中阅读。

在你踏板的地方要小心。 这是少数普遍接受的未定义行为之一,其中通常期望在没有陷阱重复的二进制补码表示中发生LIA样式包装。 重要的是要意识到存在使用与包含所有1的位表示相对应的陷阱表示的实现。

总而言之, fwrapvftrapv存在是为了向您传递一个选择,开发人员必须为您做出选择,并且选择是在发生有符号整数溢出时发生的。 当然,他们必须选择一个默认值,在您的情况下,它似乎与fwrapv而不是ftrapv相关ftrapv 。 不一定是这种情况,并且不必是这些编译器选项改变任何东西的情况。