为什么无符号整数可以小于零?

4

我刚试了一下把一个无符号整数变成小于0的数,令人惊喜的是它起作用了!

#include<stdio.h>

int main(void)
{
        unsigned int foo = 5;
        foo -= 10;
        printf("%d", foo);
        return 0;
}

编译器版本

clang -Weverything main.c

该程序返回:
-5

根据这篇文章和我的个人了解,这是不可能的。但为什么它还能工作呢?我有什么遗漏吗?这是因为未定义行为吗?还是printf?或者是其他原因?

啊,这正是我所想的。那么我该如何更改呢?它是哪个实际值,我该如何检查它? - Paul Kramme
printf效果已经得到了解答。现在,为了验证编译器并不真的认为foo是负数,请在结尾处添加以下内容:if(foo < 0) printf("this won't print"); - dxiv
哦,好吧。我的想法是将其用作机器人的状态持有者,这样我就不必保护零度以下的范围了。那么这是不可能的 :/ - Paul Kramme
3个回答

6

这个程序使用printf将一个unsigned int的值重新解释为有符号整数。尽管当unsigned int的值也适合于int时,这不是问题,但它是未定义的行为:

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

解释: 从五中减去十“环绕”,所以基于您系统对unsigned int的表示,您会得到一个大数字。结果,这个大数字的位表示对应于您系统上负五的表示,因此当printf将该值重新解释为带符号的时,就会打印出负五。

为什么编译器没有任何通知呢?

请参见此问答,可能会有解释。


4

printf()将foo的值解释为有符号整数。尝试用%u替换%d。

编辑:正如dasblinkenlight所说,这是未定义的行为。编程语言规范没有说明如果发生这种情况应该做什么,因此留给实现。有时可能会产生不同的结果,但在这种情况下,可能不会。


谢谢,为什么编译器没有任何通知呢?其他函数看到了什么? - Paul Kramme
编译器不会对所有未定义的行为发出警告。我清楚地记得曾经与某人争论过sizeof(void)的价值。Void没有大小,但我一直坚持它是4,因为我在做printf("%d\n", sizeof(void)); 另外,如果您想阅读的话,这个问题的另一个答案有关于如何将该值解释为-5的解释 :) - user6557303
未定义行为并不是显示不同的值。操作的结果是明确定义的。而 sizeof(void) 应该会生成一个错误。无论如何,%d 用于 size_t 也是未定义行为(就像 %u 一样)。 - too honest for this site
我很确定它说的是4,但我可能没有使用GCC(当时我使用的是Windows,无法记住我使用的编译器是什么。至少是2-3年前)。@Olaf谢谢你指出来,未定义行为是关于编程语言不规定行为,而不是在不同平台上有不同的结果。 - user6557303
如果您将“%d”替换为“%u”,则会得到一个值= 4294967291(根据int的大小可能会有所不同)。 - tryKuldeepTanwar
显示剩余2条评论

0
计算机上的数字以二进制补码形式存储。在无符号数范围内,用二进制补码表示的负数是有效的数字。处理器核心巧妙地实现了加法/减法/乘法/除法,使其不需要知道数字的符号即可执行这些操作。使用这个核心的C语言将这些数字传递给处理器的算术单元,该单元执行所请求的操作并返回结果。这就是为什么您在整数上得到了预期的结果。

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