-fwrapv做什么?

时间:2017-11-11 00:05:18

标签: c gcc binary

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

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

但每当我尝试溢出时,结果与fwrapv相同或不同。

4 个答案:

答案 0 :(得分:8)

想想这个功能:

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

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

当您编译而没有-fwrapv 时,编译器将假定溢出是“非包装”。它将优化该函数以始终返回1 (忽略溢出情况)。

当您使用-fwrapv 编译时,该功能不会被优化,并且它将具有添加1并比较这两个值的逻辑,因为现在,溢出就是包裹着#39; (即,溢出的数字将根据2的恭维表示进行换行。)

答案 1 :(得分:4)

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

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

答案 2 :(得分:1)

在使用新版本的 gcc 编译器时,我偶然发现了一个问题,该问题使标志 -fwrapv 变得清晰。

char Data[8]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int a=256*(256*(256*Data[3]+Data[2])+Data[1])+Data[0];

如果您使用 -fwrapv 标志,结果将为 -1。 但是你做到了吗:

if(a<0)
  printf("the value of a %d is negative\n",a);
else
  printf("the value of a %d is positive\n",a);

它将打印“a -1 的值是正数”,因为在优化时它会删除第一部分,因为正数的 + - 和 * 将始终为正数(char 是无符号的)。
当您使用 -fwrapv 标志编译时,它将打印正确答案。
如果你使用这个:

int a=(Data[3]<<24)+(Data[2]<<16)+(Data[1]<<8)+Data[0];

无论是否带有标志,代码都按预期工作。

答案 3 :(得分:0)

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

  • 实现定义,这意味着编译器开发人员必须做出选择,记录该选择并维护该批次,以便被视为符合标准的C实现。
  • C11/3.4.3 establishes a definition for undefined behaviour and gives an extremely familiar example,在使用不可移植或错误的程序构造时,远远优于我能写的任何东西:

    1 未定义的行为行为或国际标准没有要求的错误数据

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

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

还有未指明的行为,但我会将其作为练习留给您阅读标准中的内容。

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

总而言之,fwrapvftrapv存在是为了向您传递选择,开发人员必须选择以此为代表,并且选择是签名整数时会发生的情况发生溢出。当然,他们必须选择默认值,在您的情况下,默认值与fwrapv而不是ftrapv相关联。情况并非如此,并且不需要这些编译器选项更改任何的事情。