C语言中关于无符号整数的问题

3

可能是重复问题:
无符号整数和有符号字符的比较

int j = 10;
unsigned int i = 10;
if( j > -1 )
    printf("1\n");
else
    printf("2\n");
if( i > -1 )
    printf("3\n");
else
    printf("4\n");

输出结果为:
1
4

我已经跟踪了汇编代码,比较的方式类似:

cmp     dword ptr [ebp-10h],0FFFFFFFFh
...
cmp     dword ptr [ebp-14h],0FFFFFFFFh

但仍然不理解为什么一个为真,另一个为假。

我认为CPU并不知道 dword ptr 是否带符号。

那么它在底层是如何工作的呢?

更新

有人能够用汇编语言来解释吗?


1
OP的问题与提出的重复问题不同,请不要关闭。 - R.. GitHub STOP HELPING ICE
2
这个问题不应该被关闭。这个问题是关于x86汇编的。 - Neil G
7个回答

4
在下面的代码中:
if( i > -1 )
    printf("3\n");
else
    printf("4\n");

-1被转换为一个无符号整数,这是最大的无符号整数,明显比10大。

在我的机器上,以下代码

#include <stdio.h>
int main()
{
  unsigned int i = -1;
  printf("%u\n", i);
  return 0;
}

Yields: 4294967295


1
稍微解释一下,当出现有符号/无符号比较时,两种类型总是被转换为无符号。在进行这样的转换时,位表示不会改变。虽然C++标准没有指定,但最常见的有符号整数表示是二进制补码,它基本上从无符号位表示中隐含地减去了最大可能无符号值的一半。如果启用编译器警告,您的编译器应该会提醒您。 - Steve Howard
1
这个问题被标记为C语言。std::cout<iostream>不是C语言的一部分。 - Prasoon Saurav
@Prasoon:这只是为了说明将-1转换为无符号值时会发生什么。改为纯C :) - ralphtheninja
你能用汇编语言解释一下吗? - kern
@kern:抱歉,那个抽象层次还不是我的领域 :) - ralphtheninja

4
正如其他答案所说,-1被转换为无符号整数。请检查接下来的跳转指令。其中一个应该是“ja”,另一个是“jg”:无符号和有符号跳转指令。

你能详细说明一下 jajg 的事情吗? - kern
+1 这是唯一一个回答 OP 关于汇编的真正问题并指出 OP 忽略了重要指令——跳转的答案。 - R.. GitHub STOP HELPING ICE
jajnc相同 - 如果Carry标志位清除,则跳转。 jg则取决于Carry、Sign和/或Overflow标志位(我忘记了细节,但您可以查找或自行解决)来实现有符号比较,这更为复杂。 - R.. GitHub STOP HELPING ICE
@kern:汇编指令“cmp”本质上可以进行有符号和无符号比较。有一组针对有符号数的跳转语句(jg、jl、jge、jle),还有一组针对无符号数的跳转语句(ja、jb、jae、jbe)。 - Neil G

2
当比较无符号数 (如 i) 和有符号数 (如 -1) 时,编译器会将有符号值转换为无符号值。将 -1 转换为无符号值会产生一个非常大的值,因此条件 ( i > -1) 将返回 false。
[6.3.1.3/2] (C99)
如果新类型是无符号的,则通过重复添加或减去可以在新类型中表示的最大值加1,直到该值在新类型的范围内进行转换。

1

这是您的程序,其中包含所有隐式转换的注释:

  int j;
  unsigned int i;
  j = 10;
  i = (unsigned int)10;
  if (j > -1) { printf("1\n"); }
  else { printf("2\n"); }
  if (i > (unsigned int)-1) { printf("3\n"); }
  else { printf("4\n"); }

第一个比较是有符号整数的比较。第二个比较是无符号整数的比较。

关于您问题的另一方面,汇编中确实只有一条比较指令,但它设置了各种标志位。您可以测试您所感兴趣的标志位,以进行您正在进行的比较(有符号、无符号等)。


1

正如 @Neil G 指出的那样,虽然 cmp 不知道无符号和有符号,但条件跳转指令知道。

这样做是因为普通算术指令(如 sub)会影响所有相关条件码,因此您可以直接进行条件跳转,而无需进行任何显式比较。

请注意,cmp 只是一个不影响目标的 sub


0

在">"运算符被评估之前,两侧的操作数需要(概念上)转换为相同的类型。在这种情况下(不确定是否是“标准”,还是编译器的一时兴起),-1被转换为无符号数。-1当然是0xFFFFFFFF,而在无符号比较中,它比0x0000000A大,尽管在有符号比较中它更小(因为高位比特是符号位,并且有符号负数几乎总是以“二进制补码”表示)。

“在幕后”,我怀疑在您的CPU上,生成的条件代码具有有符号和无符号部分,两者之间的差异与如何检查条件代码有关,而不是比较本身。


这就是为什么在进行比较时,明确将数字转换为所需的形式总是明智的选择,而不是依赖隐式转换。很难记住所有隐式转换规则并预测它们将如何应用。 - Hot Licks

-1

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