"int" 和 "unsigned int" 的真正区别

78

整数类型 int:

32 位的 int 数据类型可以容纳在 −2,147,483,648 到 2,147,483,647 范围内的整数值。你也可以将该数据类型称为带符号的 int 或 signed。

无符号整数类型 unsigned int:

32 位的无符号整数类型可以容纳在 0 到 4,294,967,295 范围内的整数值。你也可以简单地把这个数据类型称为无符号的。

好了,但是实际上:

int x = 0xFFFFFFFF;
unsigned int y = 0xFFFFFFFF;
printf("%d, %d, %u, %u", x, y, x, y);
// -1, -1, 4294967295, 4294967295

没有区别,嗯。我有点困惑。


1
你需要考虑到 intunsigned int 的二进制表示。 - Oded
3
发生这种情况的真正原因是C语言是一种弱类型语言,但unsigned intint确实是不同的。 - cha0site
1
https://dev59.com/YnVC5IYBdhLWcg3wliKe - Rafał Rawicki
可能是为什么无符号整数0xFFFFFFFF等于有符号整数-1?的重复问题。 - Rafał Rawicki
9个回答

57

哈哈。你这里有一个隐式转换,因为你告诉 printf 期望的类型。

尝试使用这个替代方案:

unsigned int x = 0xFFFFFFFF;
int y = 0xFFFFFFFF;

if (x < 0)
    printf("one\n");
else
    printf("two\n");
if (y < 0)
    printf("three\n");
else
    printf("four\n");

32

是的,因为在您的情况下,它们使用相同的表示法

当被解释为32位带符号整数时,比特模式0xFFFFFFFF看起来像-1,而当被解释为32位无符号整数时,它看起来像4294967295。

这就像char c = 65一样。如果你将其解释为有符号整数,它是65。如果你将其解释为字符,它是a


正如R和pmg指出的那样,从技术上讲,传递不匹配格式说明符的参数是未定义行为。因此,程序可以做任何事情(从打印随机值到崩溃、打印“正确”的结果等)。

标准在7.19.6.1-9中指出:

  

如果转换说明符无效,则行为未定义。如果任何参数不是相应转换说明符的正确类型,则行为未定义。


4
真正的原因是他使用%u来打印两个值。 - shadyabhi
5
他之所以出现问题,是因为他向 printf 函数传递了一个不匹配的格式字符串,导致程序 调用未定义行为。我几乎想将那些忽略这一点而指出与所讨论问题无关的内容的答案都点踩... - R.. GitHub STOP HELPING ICE
1
@R.. 没错,格式和参数不匹配是未定义行为。随意点踩,我不介意 :-) - cnicutar
1
fprintf 的文档非常明确:“如果任何参数与相应的转换说明符不是正确的类型,则行为是未定义的。” - R.. GitHub STOP HELPING ICE
如果函数定义的类型不包括原型,并且在提升后的参数类型与提升后的参数类型不兼容,则行为是未定义的,除了以下情况:一个提升类型是有符号整数类型,另一个提升类型是相应的无符号整数类型,并且该值在两种类型中都可表示; 两种类型都是指向字符类型或void的限定或非限定版本的指针。 - Antti Haapala -- Слава Україні
显示剩余4条评论

23

两者在存储于内存与寄存器时没有区别,int寄存器中没有带符号和无符号版本,也没有带符号信息存储于int中,只有当进行数学运算时才会出现差异,CPU中内置了带符号和无符号数学运算的版本,并且符号确定了编译器应使用哪个版本。


这个答案是最简单的。 - Thanh-Nhon Nguyen

6
问题在于你调用了未定义行为
当您调用 UB 时,任何事情都可能发生。
赋值是可以的;在第一行中存在隐式转换。
int x = 0xFFFFFFFF;
unsigned int y = 0xFFFFFFFF;

然而,对于printf的调用是不正确的

printf("%d, %d, %u, %u", x, y, x, y);

如果使用%格式说明符与参数类型不匹配,就会出现错误。
在您的情况下,您按照1 int、1 unsigned int、1 int和1 unsigned int的顺序提供了2个int和2个unsigned int


不要做UB


4
错误。这不是未定义行为。请参阅C99第6.5.2.2段第6款。 - cha0site
1
@cha0site:我特别说了赋值是可以的:第6.5.2.2节第6段并不适用于我的答案。UB在printf调用中:请查看7.19.6.1:"如果任何参数与相应的转换说明符不匹配,则行为未定义"。还要注意,printf是一个接受可变数量参数的函数,并且原型的转换是不可能的。 - pmg
C99 [6.5.2.2](http://port70.net/~nsz/c/c99/n1256.html#6.5.2.2)优先于此。有整数提升的问题。我可能数错了——可能是第5段。 - cha0site
1
@cnicutar:什么?当然可以。那正是它所说的:“如果函数是用不包括原型的类型定义的,并且在提升后的参数类型与提升后的参数类型不兼容,则其行为未定义,除了以下情况:一个提升类型是带符号整数类型,另一个提升类型是相应的无符号整数类型,并且该值在两种类型中都可以表示;”。 - cha0site
@cha0site,我没有看到任何关于printf的内容,这是一个使用原型定义的函数。 - cnicutar
显示剩余2条评论

5
二进制表示是关键。例如: 十六进制下的无符号整数
 0XFFFFFFF = translates to = 1111 1111 1111 1111 1111 1111 1111 1111 

如何用十进制正数表示4,294,967,295。 但我们也需要一种表示负数的方法。 因此,大脑们决定采用二进制补码。 简而言之,他们取了最左边的位,并决定当它是 1(后跟至少一个其他位设置为 1)时,数字将为负数。 当最左边的位设为 0 时,数字为正数。 现在让我们看看会发生什么。

0000 0000 0000 0000 0000 0000 0000 0011 = 3

加上我们最终得出的数字。

0111 1111 1111 1111 1111 1111 1111 1111 = 2,147,483,645

带符号整数的最高正数。让我们再增加1个比特位(二进制加法将溢出进位到左侧,在本例中,所有比特位都设置为1,因此我们落在了最左边的比特位上)。

1111 1111 1111 1111 1111 1111 1111 1111 = -1

我想简单地说一下,两者的区别在于一个允许负数,而另一个不允许。 这是由于标志位、左侧位或最高有效位造成的。

4

intunsigned int 的内部表示相同。

因此,当您将相同的格式字符串传递给 printf 时,它们将被打印为相同的结果。

然而,在比较它们时存在差异。请考虑:

int x = 0x7FFFFFFF;
int y = 0xFFFFFFFF;
x < y // false
x > y // true
(unsigned int) x < (unsigned int y) // true
(unsigned int) x > (unsigned int y) // false

这也可能是一个警示,因为当比较有符号和无符号整数时,其中一个将隐式强制转换以匹配类型。

4

他询问的是真正的区别。 当您谈论未定义行为时,您处于语言规范提供的保证级别 - 这与现实相距甚远。 要了解真正的区别,请检查此片段(当然,这是UB,但在您喜欢的编译器上完全定义):

#include <stdio.h>

int main()
{
    int i1 = ~0;
    int i2 = i1 >> 1;
    unsigned u1 = ~0;
    unsigned u2 = u1 >> 1;
    printf("int         : %X -> %X\n", i1, i2);
    printf("unsigned int: %X -> %X\n", u1, u2);
}

2

类型仅告诉您位模式应表示什么。位只是您制作的内容。相同的序列可以以不同的方式解释。


1

printf函数根据匹配的位置中的格式说明符解释您传递给它的值。如果您告诉printf函数您传递了一个int,但实际上传递了一个unsigned,那么printf函数会重新解释其中一个为另一个,并打印您看到的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接