我认为unsigned int只能存储> = 0的整数。 但是我尝试将负数赋给一个无符号的整数,没有什么特别的事情发生。 似乎它毫无问题地存储了值。
那么signed int和unsigned int有什么区别?如果它仍然可以存储任何值,那又有什么意义呢?
答案 0 :(得分:6)
类似于
的语句unsigned int t = -1;
printf("%u", t);
是完全合法的,并且用C进行了很好的定义。将负值赋给无符号整数类型时,将隐式转换(例如,参见this在线C标准草案):
6.3.1.3有符号和无符号整数
(2)否则,如果新类型是无符号的,则将值转换为 重复加或减一个比最大值大 可以用新类型表示,直到该值在 新类型。
上面程序的输出是一个无符号值,即
4294967295
因此,您可以为无符号整数类型分配“负”值,但实际结果并不是负值。当您将无符号整数值与负值进行比较时,这尤其重要。例如,考虑以下两个循环:
int i = 10;
while (--i >= 0) { // 10 iterations
printf("i: %d\n", i);
}
unsigned int u = 10;
while (--u >= 0) { // endless loop; warning provided.
printf("u: %u\n", u);
}
第一个将在10次迭代后结束,而第二个将永无止境:无符号整数值不能变为负数,因此u >= 0
始终为真。
答案 1 :(得分:2)
在C中使用unsigned int的要点是:
答案 2 :(得分:2)
您正确的认为unsigned int
只能存储大于等于0的整数。(当然也有一个上限,该上限取决于您的体系结构,并且在限制中被定义为UINT_MAX
。 h)。
通过为int
分配一个已签名的unsigned int
值,您正在调用隐式类型转换。 C语言有一些非常精确的规则来规定这种情况。编译器会尽可能尝试保留该值。以这个为例:
int x = 5;
unsigned int y;
y = x;
上面的代码也进行类型转换,但是由于值“ 5”在有符号和无符号整数范围内均可表示,因此该值可以保留,因此y
的值也为5。 / p>
现在考虑:
x = -5;
y = x;
特别是在这种情况下,您要分配一个在unsigned int
可表示范围内的 not 值,因此编译器必须将该值转换为该范围内的值。 C标准指示将值1 + UINT_MAX
添加到该值,直到它在unsigned int
的范围内。如今,在大多数系统上,UINT_MAX
的定义为4294967925(2 ^ 32-1),因此y
的值实际上是4294967921(或十六进制的0xFFFFFFFB)。
重要的是要注意,在二进制补码机(近来普遍存在)上,signed int
值为-5的二进制表示也为0xFFFFFFFB,但这不是必需的。 C标准允许并支持使用不同整数编码的机器,因此可移植代码永远不要假定二进制表示将在诸如此类的隐式转换之后被保留。
希望这会有所帮助!
答案 3 :(得分:2)
重要的一点是,溢出有符号整数是未定义的行为,而无符号整数则被定义为环绕。实际上,当您为负数赋一个负值时就会发生这种情况:它会自动回绕直到该值在范围内。
尽管这种无符号类型的环绕行为意味着为它们分配负值确实是完全有效的,但将它们转换回带符号类型并不是很好的定义(最好是由实现定义的,在最坏的情况下是未定义的行为) ,具体取决于您的操作方式)。尽管在许多常见平台上甚至有可能在内部都相同,但有符号整数和无符号整数是相同的,但值的预期含义对于比较,转换(例如浮点数)以及编译器优化至关重要。 >
总而言之,当您需要定义明确的环绕语义来进行上溢和下溢时,和/或您需要表示大于相应(或合适的最大)有符号整数的正整数时,应使用无符号类型。类型。从技术上讲,在大多数情况下,可以通过在无符号类型之上实现负数来避免使用 signed 类型(毕竟,您可以选择将某些位模式解释为负数),但是……为什么,当语言免费提供此服务。 C语言中带符号整数的唯一真正问题是必须提防溢出,但作为回报,您可能会获得更好的优化。
答案 4 :(得分:0)
无符号数具有1)更高的最大值和2)定义的环绕溢出。
如果精度无限
(unxigned_c = unsigned_a + unsinged_b) >= UINT_MAX
然后,unsigned_c
的模数UINT_MAX+1
将减少:
#include <limits.h>
#include <stdio.h>
int main()
{
printf("%u\n", UINT_MAX+1); //prints 0
printf("%u\n", UINT_MAX+2); //prints 1
printf("%u\n", UINT_MAX+3); //prints 2
}
将带符号的值存储到无符号中时,发生了类似的事情。
在这种情况下,6.3.1.3p2适用-在概念上将UINT_MAX+1
添加到值中。)
另一方面,对于带符号的类型,溢出是不确定的,这意味着如果允许它发生,则程序将不再具有正确的格式,并且标准不能保证其行为。编译器exploit this for optimization通过假设它永远不会发生。
例如,如果您编译
#include <limits.h>
#include <stdio.h>
__attribute__((noinline,noclone)) //or skip the attr & define it in another tu
_Bool a_plus1_gt_b(int a, int b) { return a + 1 > b; }
int main()
{
printf("%d\n", a_plus1_gt_b(INT_MAX,0)); //0
printf("%d\n", INT_MAX+1); //1
}
在带有-O3
的gcc上,很可能会打印
1
-2147483648