我不擅长C语言,只是遇到了一个我不明白的问题。代码是:
int main()
{
unsigned int a = 100;
unsigned int b = 200;
float c = 2;
int result_i;
unsigned int result_u;
float result_f;
result_i = (a - b)*2;
result_u = (a - b);
result_f = (a-b)*c;
printf("%d\n", result_i);
printf("%d\n", result_u);
printf("%f\n", result_f);
return 0;
}
输出是:
-200
-100
8589934592.000000
Program ended with exit code: 0
对于(a-b)是负的而a,b是无符号的int类型,(a-b)是微不足道的。乘以浮点类型数c后,结果为8589934592.000000。我有两个问题:
首先,为什么在将int类型数字2相乘并分配给int类型数字后,结果是非平凡的?
第二,为什么result_u是非平凡的,即使(a-b)是负数且result_u是无符号的int类型?
我使用Xcode测试此代码,编译器是默认的APPLE LLVM 6.0。
谢谢!
答案 0 :(得分:5)
您认为a - b
为负数的假设完全不正确。
由于a
和b
具有unsigned int
类型,因此这两个变量的所有算术运算都在unsigned int
类型的域中执行。这同样适用于混合的“unsigned int
和int
”算术。这些操作实现模运算,模数等于UINT_MAX + 1
。
这意味着表达式a - b
生成类型为unsigned int
的结果。它是一个等于UINT_MAX + 1 - 100
的大正值。在具有32位int
的典型平台上,它是4294967296 - 100 = 4294967196
。
表达式(a - b) * 2
也会生成unsigned int
类型的结果。它也是一个很大的正值(UINT_MAX + 1 - 100
乘以2
并取模UINT_MAX + 1
)。在典型的平台上,它是4294967096
。
后一个值对于类型int
来说太大了。这意味着当您将其强制转换为变量result_i
时,会发生有符号整数溢出。赋值时有符号整数溢出的结果是实现定义。在您的情况下,result_i
最终成为-200
。它看起来“正确”,但语言无法保证这一点。 (尽管你的实施可能会保证。)
变量result_u
会收到正确的无符号结果 - 正值UINT_MAX + 1 - 100
。但是,您使用%d
中的printf
格式说明符而不是正确的%u
来打印该结果。使用unsigned int
说明符打印不适合int
范围的%d
值是违法的。出于这个原因,代码的行为是 undefined 。您在输出中看到的-100
值只是该未定义行为的一种表现形式。这个输出正式毫无意义,即使它第一眼看上去是“正确的”。
最后,变量result_f
接收(a-b)*c
表达式的“正确”结果,计算时没有溢出,因为乘法是在float
域中执行的。你看到的是我上面提到的大的正值乘以2
。它可能会舍入到float
类型的精度,这是实现定义的。确切的值为4294967196 * 2 = 8589934392
。
有人可能会争辩说,您打印的最后一个值是唯一一个正确反映无符号算术属性的值,即它是“自然地”从a - b
的实际结果中得出的。
答案 1 :(得分:0)
你在printf中得到负数,因为你已要求它打印带有%d的有符号整数。如果要查看最终得到的实际值,请使用%u。这也将告诉你如何最终得到浮点乘法的输出。