2010-03-01 221 views
11
int a = -534; 
unsigned int b = (unsigned int)a; 
printf("%d, %d", a, b); 

打印-534, -534类型转换为无符号用C

为什么不强制转换发生?

我希望它是-534, 534


如果我修改代码以

int a = -534; 
unsigned int b = (unsigned int)a; 
if(a < b) 
    printf("%d, %d", a, b); 

它不打印任何东西......毕竟a小于b

+2

你可能要检查你的编译器文档和看是否有检查printf格式说明符的选项。 gcc有-Wformat,它会在编译时警告你关于%d和%u的事情。 – 2010-03-01 12:13:19

+0

http://stackoverflow.com/questions/50605/signed-to-unsigned-conversion-in-c-is-it-always-safe – 2016-04-18 17:36:50

+0

这不应该有一个C++标签。 – Friedrich 2017-07-18 23:39:07

回答

4

首先,您不需要演员表:a的值隐式转换为unsigned int,分配为b。所以,你的语句相当于:

unsigned int b = a; 

现在,unsigned整数类型在C和C++的一个重要特性是它们的值总是在范围[0,最大],其中最大unsigned intUINT_MAX(它的定义见limits.h)。如果您分配的值不在该范围内,则会将其转换为该范围。因此,如果值为负数,则重复添加UINT_MAX+1以使其在[0,UINT_MAX]范围内。对于上面的代码,就好像我们写了:unsigned int b = (UINT_MAX + a) + 1。这不等于-a(534)。

请注意,无论底层表示法是二进制补码,1的补码还是符号幅度(或任何其他外来编码),以上都是真实的。人们可以看到的东西,如:

signed char c = -1; 
unsigned int u = c; 
printf("%u\n", u); 
assert(u == UINT_MAX); 

在一个典型的补机具有4个字节的intc0xff,并u0xffffffff。编译器必须确保将值-1分配给u时,它将转换为等于UINT_MAX的值。

现在回到您的代码中,printf格式字符串对于b是错误的。你应该使用%u。当你这样做时,你会发现它输出的值是UINT_MAX - 534 + 1而不是534

当在比较运算符<使用,因为bunsigned inta也转换为unsigned int。这与b = a给出;之前,意味着a < b为false:a作为unsigned int等于b

假设你有一个的补机,和你做:

signed char c = -1; 
unsigned char uc = c; 

比方说,机上的char(符号或无符号)为8位。然后cuc将存储下列值,位模式:

+----+------+-----------+ 
| c | -1 | 11111110 | 
+----+------+-----------+ 
| uc | 255 | 11111111 | 
+----+------+-----------+ 

注意的cuc位模式是不一样的。编译器必须确保c具有价值-1,并uc具有价值UCHAR_MAX,这是255这台机器上。

上有my answer to a question here on SO更多细节。

15

因为您使用%d进行打印。使用%u作为未签名。由于printf是一个可变参数函数,它不能知道参数的类型,而必须依赖格式说明符。正因为如此,你所做的类型转换没有效果。

+2

事实上,由于'%d',无符号值由'printf()'获得'reinterpret_cast'-ed _back_。 '%d'表示“取值并将其解释为带符号整数” – Vlad 2010-03-01 12:10:52

+0

是的。根据C标准,未定义的行为结果(通常不是可变参数函数,而是特别针对printf)。 – 2010-03-01 12:11:24

+0

为什么'if'失败?请参阅编辑过的问题。 – Lazer 2010-03-01 12:11:54

4

您在printf中的说明符是要求printf打印一个有符号整数,所以底层字节被解释为一个有符号整数。

您应该指定要使用%u的无符号整数。

编辑:a==b是比较真实的,这是奇怪的行为,但它是完全有效的。你并没有改变底层位,你只要求编译器以某种方式处理底层位。因此按位比较是正确的。

[speculation]我会怀疑编译器实现中的行为可能会有所不同 - 也就是说,假想的CPU可能不会对有符号和无符号数字使用相同的逻辑,在这种情况下,按位比较会失败。 [/speculation]

+1

我不认为你写了你的意思写:) – 2010-03-01 12:07:10

+0

是的,这是正确的,加上我诵读困难嘿嘿。 – 2010-03-01 12:11:09

+0

为什么'if'失败?请参阅编辑的问题。 – Lazer 2010-03-01 12:12:32

0

我想为什么b被打印为-534的第一个案例已被Tronic和Hassan充分回答。你不应该使用%d,而应该使用%u。

就你的第二种情况而言,隐含的类型转换将会发生,并且a和b都将是相同的,因为你的比较确实会产生预期的结果。

0

就我所见,if因为编译器假定第二个变量应该被视为与第一个相同的类型而失败。尝试 if(b> a) 查看差异。

+0

这个答案是错误的:在一个测试比较一个带符号的int和一个无符号的int,signed int被转换为一个unsigned int。 (a < b) and (a > b)都是错误的,因为(a == b)。 – 2010-03-02 19:21:46

+0

Both(a < b) and (a > b)are false 但是两者都不是if(b> a)。我的编译器同意。 – 2010-03-02 22:43:54

0

Re第二个问题: 比较在两种不同类型之间永远不起作用 - 它们总是被隐式转换为“最低公分母”,在这种情况下,它将是unsigned int。我知道,讨厌和反直觉。

+0

如果是这种情况,不应该将-534归零,否则会导致运行时错误?我在VS2008中编译了代码,'a == b'是真的。 – 2010-03-01 12:27:02

+0

否 - 在两种情况下(初始化'b'的情况#1,比较'a'和'b'的情况#2')允许-534到'unsigned int'的强制转换。 – MSalters 2010-03-01 13:09:42

1

有时C可能是一只丑陋的野兽。问题是-534始终表示值0xfffffdea,无论它存储在类型为unsigned int还是signed int的变量中。为了比较这些变量,它们必须是相同的类型,以便将自动转换为无符号或有符号的int以匹配另一个。一旦它们是相同的类型,它们是相等的,因为它们表示相同的值。

这很可能是你想要的行为是由函数abs提供:

int a = -534; 
int b = abs(a); 
printf("%d, %d", a, b); 
+0

请注意,0xfffffdea中f的个数可能会有所不同。例如。在16位的机器上它只是'0xfdea',而在64位的机器上你会有13个f,后面跟着'dea'。 – MSalters 2010-03-01 13:10:57

+0

绝对正确。我想,当我写出答案时,我应该提及一些关于int大小的问题,但我认为它可能会混淆与那些与手头问题无关的信息。 – 2010-03-01 17:47:51

+2

更重要的是,即使'dea'部分在符号量级或补码机器上不一定是正确的。 – 2010-03-01 22:37:07

0

铸造整数类型从符号到无符号不修改的位模式,它只是改变了位模式的解释。

你也有一个格式说明不匹配,%U应使用无符号整数,但即使如此,其结果将不会是534如您所愿,但4294966762.

如果你想为负值正,简单地否定它所:

unsigned b = (unsigned)-a ; 
printf("%d, %u", a, b); 

至于第二实例,类型具有不同的签名岬之间操作涉及神秘的隐式转换的规则 - 避免。您应该将编译器的警告级别设置得很高,以捕获许多这些错误。我建议/ W4/WX在VC++和-Wall -Werror -Wformat for GCC为例。

相关问题