-fwrapv做什么?
任何人都可以提供一些代码示例,当使用fwrapv vs编译时,它们的行为会有所不同。
它说–fwrapv应该“假设加法,减法和乘法的带符号算术溢出,使用二进制补码表示包装。”
但每当我尝试溢出时,无论有没有fwrapv,结果都是一样的。
想想这个function:
int f(int i) { return i+1 > i; }
从数学上讲,对于任何整数i
, i+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的位表示相对应的陷阱表示的实现。
总而言之, fwrapv
和ftrapv
存在是为了向您传递一个选择,开发人员必须为您做出选择,并且选择是在发生有符号整数溢出时发生的。 当然,他们必须选择一个默认值,在您的情况下,它似乎与fwrapv
而不是ftrapv
相关ftrapv
。 不一定是这种情况,并且不必是这些编译器选项改变任何东西的情况。