我一直在读这本书 K& R的C编程语言,并且遇到了这个。
当涉及无符号操作数时,转换规则会更复杂。问题 是有符号值和无符号值之间的比较是机器相关的,因为 它们取决于各种整数类型的大小。例如,假设int是16位而long是32位。然后 -1L< 1U ,因为1U(无符号整数)被提升为有符号长整数。但 -1L> 1UL 因为-1L被提升为无符号长并因此出现 是一个很大的正数。
从我的C开始,我对这个无符号和有符号的值没什么困难。任何人都可以解释一个无符号值如何转换为有符号值,反之亦然。[/ p>
答案 0 :(得分:3)
您已经使用整体促销触及常规算术转换的表面(较新的标准使用整数促销的术语)。
一般来说,当你有表达式,涉及算术或逻辑运算符和操作数不匹配时,则需要将它们统一(如我们所说的提升)为通用形式。在整数操作数之间的 C90 规则中(我有意跳过类型的规则"更低的"为了简单起见,int
这里)处于以下渐变中(请注意,没有{ {1}}输入C90):
long long int
→int
→unsigned int
→long int
但是,unsigned long int
和unsigned int
之间存在一个例外。如果它们具有相同的大小(以位为单位),则这些类型的两个操作数都将提升为公共类型long int
。在任何其他情况下,常见类型是在右侧(例如,当您拥有unsigned long int
和int
个操作数时,则第一个类型被提升为unsigned int
)。如果两个操作数具有相同的类型,则不会对上面列出的类型进行升级。
unsigned int
和-1L < 1U
在您的情况下,它假设-1L > 1UL
和sizeof(int) == 16
,因此:
sizeof(long) = 32
第二个操作数被提升为-1L < 1U
long int
第一个操作数被提升为-1L > 1UL
在前一个示例中,表达式的值unsigned long
为1
。在后一种情况下,当-1L < 1L
是-1L
类型的最大值(在您的情况下为n+1
时),n
会通过重复添加或减去unsigned long
而提升为无符号类型),产生大数(即n+1 == 2^32
),因此整个表达式的值也是2^32-1
(类型1
)。
答案 1 :(得分:1)
哼......有一个更长(详细)的答案,还有一个较短的(近似的)答案......
标准(C99,6.3.1)指定整数转换等级,并且其中两个整数操作数具有不同的等级,其中一个将被转换。 rank 与整数的大小有关;较大的尺寸比较小的尺寸具有更大的等级在大小相同的情况下,排名可能会有所不同 - 但这一点并不重要。但是,有符号整数和相同大小的无符号整数具有相同的 rank 。
值得记住的是,根据定义,已签名的int
和unsigned int
具有相同的大小。它们是标准调用对应的整数类型(C99,6.2.5)的示例。每个有符号整数类型都有一个相应的无符号整数类型,反之亦然 - 除了没有符号的_Bool
(并且,FWIW具有最低等级)。如上所述,对应的有符号和无符号类型具有相同的等级。
C非常喜欢int
和unsigned int
。如果提升的整数是无符号的并且大小与int
相同,它会将较小的 rank 的整数提升为unsigned int
(或int
。这称为整数提升(它是所有可能的转换的子集)。对于许多运算符,C将在执行任何其他操作之前对两个操作数执行整数提升。它还将对printf()
。
现在我们进入所谓的通常的算术转换(C99,6.3.1.8),它对于两个整数操作数是:
根据需要提升这两个参数,因此至少会int
或unsigned int
如果他们现在不相同的类型(类型,而不是等级):
如果两种类型都已签名,或两种类型都未签名,则较低的等级会转换为较高的类型。
这很简单,显然没有丢失签名。
...否则,对于不同类型,不同的签名,大小很重要:
如果带符号的操作数具有更大的大小(因此 rank ),则无符号操作数将转换为已签名操作数的类型。
这也很简单,无符号值可以在签名类型中无损失地表示。
如果无符号操作数具有更大的大小(因此 rank ),则带符号的操作数将转换为无符号操作数的类型。
因此,对于(比方说)unsigned long long
和int
(假设其大小不同),通过向int
添加ULLONG_MAX + 1
来转换int
。 / p>
这不是那么简单,签名就失去了。
...否则,对于不同类型,具有相同大小的不同签名,结果将是其当前大小的两个无符号操作数。 rank 开始决定操作数的类型:
如果操作数具有相同的 rank ,则签名的操作数将转换为无符号操作数的类型。
如果您拥有unsigned int
和int
个操作数,UINT_MAX + 1
已转换(通过向其添加int
),就会发生这种情况。
如果无符号操作数具有更大的 rank ,则带符号的操作数将转换为无符号操作数的类型(如上所述)。
否则,带符号的操作数具有更大的 rank ,并且两个操作数都将转换为与带符号操作数的类型对应的无符号类型(即具有更大的无符号类型)等级)。
这一切看起来都非常复杂:-(但是,请记住,整数的大小是其 rank 的主要组成部分 - 这使得处理相同大小的整数看起来很复杂,但签名和类型不同。
(对于挑剔:我已经掩盖了标准中的皱纹,允许整数表示包括“填充”位 - 这是非常奇特的 - 所以,对于大小读取宽度,在需要的地方。)
升级到unsigned int
或-1L > 0UL
后:
如果操作数具有不同的大小,则转换得越小,如果大于未签名但较小则不转换,则较小的则丢失其签名。
如果操作数具有相同的大小但签名不同,则签名的操作数将转换为无符号,失去签名。
转换为无符号(丢失有符号)需要添加MAX + 1,其中MAX是转换为无符号类型的最大值。
因此:-1L == ULONG_MAX
......确实是{{1}}。