有符号整数和无符号整数的区别是什么?

151

有符号整数(signed int)和无符号整数(unsigned int)之间有什么区别?


8
这是一个真正的问题,答案并不那么简单,而是相当微妙。 - R.. GitHub STOP HELPING ICE
2
投票重新开放。这可能是一个重复的问题,但它绝对是一个真实的问题。 - Brian
5
回复:“它可能是一个重复问题” - 在C语言中,unsigned int和signed int有什么区别?在C语言中,signed int和unsigned int都是整数类型,但signed int可以表示正数、负数和零,而unsigned int只能表示非负数(即正数和零)。因此,在使用这些类型时需要注意溢出和符号扩展的问题。 - eldarerathis
1
应添加更多标签,因为许多编程语言都在使用它们。 - Juan Boero
1
这个问题可能需要一个章节来详细阐述。如果你想深入了解,可以查看无符号和有符号整数获取更多的解释。 - anonymous
5个回答

196

如您所知,int在内部存储为二进制。通常,一个int包含 32 位,但在某些环境中可能包含 16 或 64 位(或者甚至不同的数量,通常但不一定是2的幂)。

但对于此示例,让我们看看 4 位整数。虽然很小,但用于说明目的非常有用。

由于这种整数有四个位,因此它可以假定 16 个值; 16 是 2 的四次幂,或者 2 乘以 2 乘以 2 乘以 2。这些值是什么?答案取决于这个整数是一个 signed int 还是 unsigned int。使用 unsigned int,该值永远不会为负;没有与该值相关联的符号。下面是四位 unsigned int 的 16 个可能值:

bits  value
0000    0
0001    1
0010    2
0011    3
0100    4
0101    5
0110    6
0111    7
1000    8
1001    9
1010   10
1011   11
1100   12
1101   13
1110   14
1111   15

...这里是四位有符号int的16个可能值:

bits  value
0000    0
0001    1
0010    2
0011    3
0100    4
0101    5
0110    6
0111    7
1000   -8
1001   -7
1010   -6
1011   -5
1100   -4
1101   -3
1110   -2
1111   -1

如您所见,对于signed int,最高位只有在数字为负数时才是 1,这就是为什么对于signed int,这一位被称为“符号位”的原因。


18
值得指出的是,这是二进制补码格式,目前被广泛使用。还有其他表示带符号整数的方法,最著名的是反码表示法。 - Schedler
1
虽然不要求使用二进制补码,但是(unsigned)(-1)被要求作为unsigned类型的最大可表示值(与二进制表示无关),这在二进制补码中显然成立,但在其他表示中可能不成立。 - rubenvb
4
标准规定有三种允许的表示方式来表示带符号整数:符号+大小法、二进制补码和一进制补码。除此之外的任何其他表示方式都必须对程序是不可见的,并被视为这三种表示方式之一。 - Alexey Frunze
@Mihail Georgescu:我不确定你在问什么。无符号1111 - 0001 = 1110:15 - 1 = 14。有符号1111 - 0001 = 1110:-1 - 1 = -2。加法和减法无论是有符号还是无符号都是一样的。如果机器同时具有有符号和无符号整数溢出检测,则它们之间当然会有所不同。 - Bill Evans at Mariposa
1
有符号整数和无符号整数之间的区别不仅在语言中实现,而且在CPU本身也有支持。如果您添加两个整数,可以使用一个CPU标志(或一组标志)检查无符号溢出,并使用另一个CPU标志(或一组标志)检查有符号溢出。同样,如果您比较两个数字,可以使用一个CPU标志(或一组标志)检查无符号结果,并使用另一个CPU标志(或一组标志)检查有符号结果。 - Bill Evans at Mariposa
显示剩余3条评论

91

简单来说,无符号整数是一个不可能为负的整数,因此它可以承载更大范围内的正值。有符号整数允许为负,但为了能承载更多的负值,会牺牲一部分正数取值范围。


15
有时像这样简单的答案可以很好地解释这个问题。 - nishchalpro
最好的解释,用简单的语言! - Vikingslord
请注意,无符号整数的正值范围不一定比有符号整数更高。标准仅保证所有有符号类型的非负值可以存储在相应的无符号类型中。 - martinkunev

27

intunsigned int是两种不同的整数类型。(int也可以称为signed int或者只是signed; unsigned int也可以称为unsigned。)

正如名称所示,int是一种有符号整数类型,而unsigned int是一种无符号整数类型。这意味着int能够表示负值,而unsigned int只能表示非负值。

C语言对这些类型的范围施加了一些要求。int的范围必须至少为-32767 .. +32767unsigned int的范围必须至少为0 .. 65535。这意味着这两种类型必须至少为16位。它们在许多系统上是32位甚至64位。int通常具有额外的负值,因为大多数现代系统使用的是二进制补码表示方法。

也许最重要的区别是有符号和无符号算术的行为。对于有符号的int,溢出的行为是未定义的。对于unsigned int,没有溢出;任何产生类型范围外值的操作都会回绕,因此例如UINT_MAX + 1U == 0U

任何整数类型,无论是有符号还是无符号,都模拟了数学整数的一个子范围。只要你使用的是类型范围内的值,一切都有效。当你接近类型的下限或上限时,你会遇到不连续性,并且可能会得到意外结果。对于有符号整数类型,问题仅出现在非常大的负值和正值超过INT_MININT_MAX时。对于无符号整数类型,问题发生在非常大的正值和零处。这可能是错误的源头。例如,这是一个无限循环:

for (unsigned int i = 10; i >= 0; i --) {
    printf("%u\n", i);
}

因为i始终大于或等于零;这是无符号类型的特性。(在循环内部,当i为零时,i--会将其值设置为UINT_MAX。)


15
有时我们提前知道存储在给定整数变量中的值将始终为正 - 例如,当它仅用于计数时。在这种情况下,我们可以将变量声明为无符号的,如unsigned int num_student;。使用这样的声明,允许的整数值范围(对于32位编译器)将从范围-2147483648到+2147483647移动到范围0到4294967295。因此,将整数声明为无符号几乎会使它可以容纳的最大可能值的大小增加一倍。

@Alex 我在10分钟前正在编辑那个答案,结果它和原来一模一样。哈哈 - Stephen

-2

实际上,有两个区别:

  1. 打印(例如在 C++ 中使用 cout 或在 C 中使用 printf):无符号整数比特表示被打印函数解释为非负整数。
  2. 排序:排序取决于有符号或无符号的规定。

该代码可以使用排序标准识别整数:

char a = 0;
a--;
if (0 < a)
    printf("unsigned");
else
    printf("signed");

char 在某些编译器中被认为是 signed,而在其他编译器中则被认为是 unsigned。上面的代码使用排序准则来确定编译器中哪个被认为是。如果 a 是无符号的,在执行 a-- 后,它将大于 0,但如果它是有符号的,则会小于零。但在两种情况下,a 的位表示都是相同的。也就是说,在两种情况下,a-- 对位表示进行了相同的更改。


如果能解释一下它们在处理负数时的不同,那将会对这篇文章有很大帮助。 - Daniel Jackson
@DanielJackson 不太清楚你的意思。一个字符可以根据编译器被视为负数或正数。代码的输出取决于编译器的选择,这显示了有符号和无符号之间的差异。 - Minimus Heximus

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