C语言 无符号整数和有符号整数的比较操作

brgchamk  于 2023-05-28  发布在  其他
关注(0)|答案(8)|浏览(316)

查看此代码片段

int main()
{ 
 unsigned int a = 1000;
 int b = -1;
 if (a>b) printf("A is BIG! %d\n", a-b);
 else printf("a is SMALL! %d\n", a-b); 
 return 0;
}

这给出了输出:a是小的:1001
我不明白这是怎么回事>操作符在这里是如何工作的?为什么“a”比“B”小?如果它确实更小,为什么我得到一个正数(1001)作为差值?

iq3niunx

iq3niunx1#

不同整数类型之间的二进制运算是在一个“公共”类型中执行的,该类型由所谓的 * 通常的算术转换 * 定义(参见语言规范,6.3.1.8)。在您的例子中,“公共”类型是unsigned int。这意味着int操作数(您的b)将在比较之前转换为unsigned int,以及用于执行减法。
-1转换为unsigned int时,结果是最大可能的unsigned int值(与UINT_MAX相同)。不用说,它将大于您的无符号1000值,这意味着a > b确实为false,并且a(unsigned) b相比确实 * 小 *。代码中的if应该解析为else分支,这是您在实验中观察到的。
相同的转换规则适用于减法。您的a-b实际上被解释为a - (unsigned) b,结果的类型为unsigned int。这样的值不能用%d说明符打印,因为%d只适用于 signed 值。尝试使用%d打印它会导致未定义的行为,因此从C语言的Angular 来看,您看到打印的值(即使它在实践中具有逻辑确定性解释)完全没有意义。

**编辑:**实际上,我可能对未定义的行为部分是错误的。根据C语言规范,相应的有符号和无符号整数类型的范围的公共部分应具有相同的表示(根据脚注31,这意味着“作为函数参数的可互换性”)。因此,a - b表达式的结果是如上所述的无符号1001,除非我遗漏了什么,否则使用%d说明符打印这个特定的无符号值是法律的的,因为它福尔斯在int的正范围内。使用%d打印(unsigned) INT_MAX + 1是未定义的,但1001u是可以的。

p3rjfoxz

p3rjfoxz2#

int为32位的典型实现中,-1转换为unsigned int时为4,294,967,295,实际上≥ 1000。
即使你在unsigned的世界里处理减法,你得到的是1000 - (4,294,967,295) = -4,294,966,295 = 1,001
这就是为什么当您将unsignedsigned进行比较时,gcc会发出警告的原因。(如果没有看到警告,请传递-Wsign-compare标志。)

mrzz3bfm

mrzz3bfm3#

您正在进行无符号比较,即。比较1000和2^32 - 1。
由于printf中的%d,输出已签名。
注意:有时候,混合使用有符号和无符号操作数的行为是特定于编译器的。我认为最好避免它们,在有疑问的时候做模型。

kb5ga3dv

kb5ga3dv4#

#include<stdio.h>
 int main()
 {
   int a = 1000;
   signed int b = -1, c = -2;
   printf("%d",(unsigned int)b);
   printf("%d\n",(unsigned int)c);
   printf("%d\n",(unsigned int)a);

   if(1000>-1){
      printf("\ntrue");
   }
   else 
     printf("\nfalse");
     return 0;
 }

为此,您需要了解运算符的优先级
1.关系运算符从左到右工作…所以当它来临的时候
如果(1000>-1)
然后首先它会把-1变成unsigned integer因为int默认被当作unsigned number并且它的取值范围大于signed number
-1会变成无符号数,它会变成一个很大的数

aij0ehis

aij0ehis5#

找到一种简单的方法来比较,当你无法摆脱unsigned声明时可能会有用,(例如,[NSArray count]),只需将“unsigned int”强制为“int”。
如果我说错了请纠正我。

if (((int)a)>b) {
    ....
}
3qpi33ja

3qpi33ja6#

硬件设计用于比较有符号与有符号以及无符号与无符号。
如果需要算术结果,请先将无符号值转换为更大的有符号类型。否则,编译器将假设比较实际上是在无符号值之间进行的。
-1表示为1111..1111,所以它是一个很大的量...最大的...当被解释为无符号时。

fwzugrvs

fwzugrvs7#

我目前正在学习x86汇编语言(https://www.udemy.com/course/x86-64-bit-assembly-language-step-by-step-tutorial/),并从汇编指令中获得了一些新的视角。
在执行a > b比较时,程序集执行如下操作:

cmp a, b
ja jump_to_some_code

cmp是汇编指令,做减法,类似于sub,设置eflags,但不存储减法结果。
并且有2组跳转指令,使用eflags的标志来确定a是否(严格)大于b

  • ja, jb, ...用于在二进制中将2个操作数视为无符号时进行比较
  • jg, jl, ...用于在以二进制形式查看2个操作数时进行比较

计算机本身并不知道或关心二进制文件代表什么。如果用户将两者都作为有符号int并希望得到正确的比较结果,则需要使用相应的jg, jl指令。
对于有符号整型,我们使用最高有效位来指示二进制是正还是负,所以本质上,你不应该也不能比较有符号和无符号类型。这就是背后的逻辑。如果你比较,你就打破了这个系统。
所以我猜C所做的是,汇编器生成相应的j...指令,用于有符号和无符号比较。但是当用户比较signed和unsigned时,它是错误的,没有人知道该怎么做,所以C“制定”了一条规则,生成ja, jb系列指令。所以这只是一个自我定义的错误规则。
JA(如果高于,则跳转):
“JA”指令执行无符号比较,并且如果进位标志(CF)被清除(0)并且零标志(ZF)被清除(0),则跳转到指定的标签。换句话说,如果第一个值严格大于第二个值,它就会跳转。
我建议学习一些汇编以更好地理解这一点。我选的课程很好,你可以试试。
如果我说错了请纠正我。

cu6pst1q

cu6pst1q8#

当比较a>B时,其中a是unsigned int类型,b是int类型,b是转换为unsigned int的类型,因此,有符号的int值-1被转换为无符号的MAX值**(范围:0到(2^32)-1)因此,a>B,即(1000>4294967296)变为假。因此else循环printf(“a is SMALL!%d\n”,a-b);**已执行。

相关问题