请考虑以下事项:
#include <iostream>
int main() {
unsigned int x = 3;
unsigned int y = 5;
std::cout << "a: " << x - y << std::endl;
std::cout << "b: " << ((int)x) - y << std::endl;
std::cout << "c: " << x - ((int)y) << std::endl;
std::cout << "d: " << ((int)x) - ((int)y) << std::endl;
}
$ g++ -Wconversion -Wall uint_stackoverflow.cc -o uint_stackoverflow && ./uint_stackoverflow
a: 4294967294
b: 4294967294
c: 4294967294
d: -2
我理解为什么“a”没有给出预期的结果。但为什么“b”和“c”失败让我感到困惑。对于“b”,我认为在将“x”转换为“int”后,结果将再次为“int”。
你能告诉我吗?编辑:编译器不应该发出警告吗? g ++(Ubuntu / Linaro 4.4.4-14ubuntu5)4.4.5
谢谢,
有人
答案 0 :(得分:26)
在算术运算中,如果任何操作数为unsigned
,则其他操作数将转换为unsigned
(如果其signed
),并且结果为操作也将unsigned
。
此外,将unsigned
强制转换为signed
然后执行操作不会更改操作数的位表示形式。在二进制补码架构上(即几乎每个现代架构),(int)x
具有与x
相同的位表示,只有它们的解释在计算值时才会改变十进制。但重要的是,算术运算是在位表示上执行的( <十字系统中的值)。并且由于转换不会改变位表示,结果的位表示也不会改变。
C ++ 03标准在§5/ 9中说:
许多期望的二元运算符 算术或枚举的操作数 类型导致转换和产量 结果类型以类似的方式。该 目的是产生一个共同的类型, 这也是结果的类型。 这种模式通常被称为 算术转换,是 定义如下:
[...]
否则,如果任一操作数为 未签名,另一个应转换 未签名。
答案 1 :(得分:10)
像往常一样引用标准......
对于C ++ 98,§[expr] / 9:
许多期望的二元运算符 算术或枚举的操作数 类型导致转换和产量 结果类型以类似的方式。该 目的是产生一个共同的类型, 这也是结果的类型。 这种模式通常称为 算术转换,它们是 定义如下:
- 如果任一操作数的类型为
long double
,则另一个操作数将被转换 到long double
。- 否则,如果任一操作数为
double
,则另一个操作数将被转换 到double
。- 否则,如果任一操作数为
float
,则另一个操作数将被转换 到float
。- 否则,应对两者进行整体促销(4.5) 操作数。 54)
- 然后,如果任一操作数为
unsigned long
,则另一个操作数将转换为unsigned long
。- 否则,如果一个操作数是
long int
而另一个是unsigned int
, 那么long int
可以代表所有人 一个unsigned int
的值,unsigned int
应转换为long int
;否则两个操作数 应转换为unsigned long int
。- 否则,如果任一操作数为
long
,则另一个操作数将被转换 到long
。- 否则,如果任一操作数为
unsigned
,则另一个操作数为unsigned
已转换为int
。[注:否则,唯一剩下的 case是两个操作数都是
long double
]
基本上,它可以概括为
double
&gt; float
&gt; unsigned long
&gt; long
&gt; unsigned
&gt; int
&gt; int
int
的类型将转换为int
)在第5项之后,对于C ++ 0x(§[expr] / 10)更改了文本,但对OP代码的影响是相同的:unsigned
将转换为{{1}} }。
答案 2 :(得分:3)
这是因为在执行隐式转换时存在数据类型的层次结构,无符号整数的优先级高于有符号整数,因此b和c将被转换为无符号整数,这就是为什么你看到的结果。< / p>
如果您不确定类型但知道所需结果的类型,那么您应该像在d中那样投射x和y。
这对类型转换有一个非常好的解释:
http://www.learncpp.com/cpp-tutorial/44-type-conversion-and-casting/