在K& R ANSI C书中,A.7.4.5节(一元减号运算符)中说明:
...无符号数量的负数是通过从提升类型的最大值中减去提升值并加1来计算的; ...
这究竟是如何计算的?你能举一个简短的例子吗?
我不知道这怎么会产生200u的负数:从任何整数类型的最大值减去200(有符号或无符号),加1不会导致-200.
我知道一元减号的作用 - 问题是我没有看到根据描述如何计算结果。
答案 0 :(得分:8)
无符号值不能 为负数,因此-200不是可能的结果。
它的含义是,如果您的系统上UINT_MAX
为65535,那么结果为:
unsigned a = 200;
unsigned b = -a;
long c = -a;
将在b
和c
中留下65336。
如果您的系统有UINT_MAX > LONG_MAX
(通常因为int
和long
大小相同),您需要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");
(请注意,我们不会将否定运算符的结果存储在任何地方 - 这不是问题。)
答案 1 :(得分:7)
显然,您在所引用的说明中错过了 unsigned 这个词。这是本案的关键词。在C语言中,无符号数量的“负数”仍然是无符号的,这意味着它实际上并不是负数。根据定义,无符号值不能是负数。它们总是正数或0. C中无符号值的算术是模运算,或者简单来说,当你对它们执行算术运算时,无符号量“环绕”。一元否定也不例外。在-n
未签名时计算n
与计算0 - n
没有区别。如果n
为unsigned int
且其值为200
,则预期结果不是-200
,而是UINT_MAX - 200 + 1
,这正是引用告诉您的内容。
答案 2 :(得分:6)
它描述了实现modular arithmetic的操作,即它计算一个值
a + (-a) == 0
这使得否定的无符号数与非有符号数一致。
在数字表示为two's complement的机器上(例如x86),只需将无符号数的位模式视为普通有符号数,并使用机器的标准“否定”指令即可。
答案 3 :(得分:4)
答案 4 :(得分:4)
无符号整数类型的运算使用模运算。算术模m与常规算术非常相似,除非结果是除以m时的正余数,如果你还没有在学校遇到它(更多细节请参见Wikipedia article。例如, 7 - 3模10是4,而3 - 7模10是6,因为3 - 7是-4,除以10得到商-1和余数6(它也可以用商表示0的余数和-4的余数,但这不是它在模运算中的工作方式。模m的可能整数值是从0到m-1的整数,包括0和m-1。负值是不可能的,-200不是在任何情况下都是有效的无符号值。
现在,一元减号表示负数,它不是模数为m的有效值。在这种情况下,我们知道它介于0和m-1之间,因为我们从无符号整数开始。因此,我们正在考虑将-k除以m。由于一个可能的值是0和-k的余数的商,另一个可能是-1的商和m-k的余数,因此正确的答案是m-k。
C中的无符号整数通常用最大值而不是模数来描述,这意味着无符号16位数通常被描述为0到65535,或者最大值为65535.这描述了通过指定m-1而不是m来确定值。
你所引用的是什么,通过从m-1减去然后加1来得到负值,所以-k是m-1-k + 1,即m-k。描述有点迂回,但它根据预先存在的定义指定了正确的结果。
答案 5 :(得分:3)
让我们保持简单,看一下无符号字符... 8位,值范围为0-255。
什么是(unsigned char)-10以及它是如何计算的?
走过K&amp;你引用的R语句,我们有:
-10的提升值从 10 中减去 提升类型的最大值是 255 加上 1 = 246
所以(unsigned char)-10实际上是246.这有意义吗?