在C中,如何计算无符号数量的负数

在K&R ANSI C一书中,A.7.4.5(一元减号运算符)一节说明:

…无符号数量的负数是通过从提升类型的最大值中减去提升值并加1来计算的; …

这究竟是如何计算的? 你能举一个简短的例子吗?

我不知道这怎么会产生200u的负数:从任何整数类型(有符号或无符号)的最大值减去200,并且加1不会导致-200。

我知道一元减去了什么 – 问题是我没有看到根据描述如何计算结果。

显然你错过了你引用的描述中的unsigned这个词。 这是本案的关键词。 在C语言中, 无符号数量的“负数”仍然是无符号的,这意味着它不是真正的负数。 根据定义,无符号值不能为负数。 它们总是正数或0. C中无符号值的算术是模运算,或者简单来说,当你对它们执行算术运算时,无符号数量“环绕”。 一元否定也不例外。 当n无符号时计算-n与计算0 - n没有区别。 如果nunsigned int且其值为200则预期结果不是-200 ,而是UINT_MAX - 200 + 1 ,这正是引用告诉您的内容。

无符号值不能负数,因此-200不是可能的结果。

它的UINT_MAX是,如果系统上的UINT_MAX为65535,则结果为:

 unsigned a = 200; unsigned b = -a; long c = -a; 

将在bc留下65336。

如果你的系统有UINT_MAX > LONG_MAX (通常因为intlong的大小相同),你需要使用long long来表示c (尽管注意甚至没有任何保证足够长)。

这个细节(否定无符号数的结果是另一个,必然是正数,无符号数)如果你不理解它会导致一些意想不到的效果。 例如,在此代码中,第一个示例打印"true"但第二个示例打印"false"

 int a = 200; unsigned b = 200; if (-a < 100) printf("true\n"); else printf("false\n"); if (-b < 100) printf("true\n"); else printf("false\n"); 

(请注意,我们不会将否定运算符的结果存储在任何地方 - 这不是问题)。

它描述了实现模运算的操作,即它计算一个这样的值

 a + (-a) == 0 

这使得否定的无符号数与非有符号数一致。

在数字表示为二进制补码的机器上 (例如x86),只需将无符号数的位模式视为普通有符号数,并使用机器的标准“否定”指令即可。

另一个问题已经触及了这个话题

 unsigned char i = -10; printf("%u\n",i); 

结果

 246 

无符号整数类型的运算使用模运算。 算术模m与常规算法非常相似,除非结果是除以m时的正余数,如果你还没有在学校遇到它(更多细节,请参阅维基百科文章 。例如,7 – 3模10是4,而3 – 7模10是6,因为3 – 7是-4,除以10得到-1的商和6的余数(它也可以用0和0的商表示)模数运算的余数为-4,但这不是它的工作原理。模m的可能整数值是从0到m-1的整数,包括0和m-1。负值是不可能的,-200不是有效的无符号值任何情况下的价值。

现在,一元减号表示负数,它不是模数为m的有效值。 在这种情况下,我们知道它介于0和m-1之间,因为我们从无符号整数开始。 因此,我们正在考虑将-k除以m。 由于一个可能的值是0和-k的余数的商,另一个可能是-1的商和mk的余数,因此正确的答案是mk。

C中的无符号整数通常用最大值而不是模数来描述,这意味着无符号16位数通常被描述为0到65535,或者最大值为65535.这是通过指定来描述值m-1而不是m。

你所引用的是什么说,通过从m-1减去它然后加1来得到负值,所以-k是m – 1 – k + 1,即m – k。 描述有点迂回,但它根据预先存在的定义指定了正确的结果。

让我们保持简单,看一下无符号字符… 8位,值范围为0-255。

什么是(unsigned char)-10以及它是如何计算的?

按照您引用的K&R声明,我们有:

从提升类型的最大值中减去-10的提升值是101 = 246

所以(unsigned char)-10实际上是246.这有意义吗?