【问题标题】:clarity of -1L < 1U and -1L > 1UL-1L < 1U 和 -1L > 1UL 的净度
【发布时间】:2014-07-31 08:44:21
【问题描述】:

我一直在阅读K&R 的《C 编程语言》一书,并遇到了这个问题。


当涉及无符号操作数时,转换规则更加复杂。问题 是有符号值和无符号值之间的比较是机器相关的,因为 它们取决于各种整数类型的大小。例如,假设 int 为 16 位,long 为 32 位。然后-1L ,因为1U,这是一个无符号整数,被提升为一个有符号长整数。但是 -1L > 1UL 因为 -1L 被提升为 unsigned long 从而出现 是一个很大的正数。


从我开始使用 C 开始,我对这个 Unsigned 和 Signed 值没有什么困难。谁能解释一下无符号值如何转换为有符号值,反之亦然。

【问题讨论】:

  • +1 有详细解释的权威链接来了!我没有。虽然 K&R 文本对我来说非常有意义,但我知道它是如何让初学者感到困惑的。
  • 如果您正在寻找有符号值与无符号值的解释,那么您绝对可以在任何 C 介绍中找到它。我不太确定这里的问题是什么……

标签: c


【解决方案1】:

您已经通过整数提升触及了普通算术转换的表面(较新的标准使用整数提升的术语)。

这些常用的算术转换是什么?

一般来说,当您有表达式时,涉及算术或逻辑运算符并且操作数不匹配,那么它们需要统一(如我们所说的提升)为通用形式。在 C90 中,整数操作数之间的规则(为了简单起见,我故意跳过“低于”int 的类型的规则)处于以下渐变中(请注意,C90 中没有 long long int 类型):

intunsigned intlong intunsigned long int

但是,unsigned intlong int 之间有一个例外。如果它们具有相同的大小(以位为单位),则此类类型的两个操作数都将提升为公共类型unsigned long int。在任何其他情况下,常见类型是右侧(例如,当您有 intunsigned int 操作数时,第一个被提升为 unsigned int)。如果两个操作数的类型相同,则不会对上面列出的类型进行提升。

这与-1L &lt; 1U-1L &gt; 1UL 有什么关系?

在您的情况下,假设 sizeof(int) == 16sizeof(long) = 32,因此:

  • -1L &lt; 1U 第二个操作数提升为 long int
  • -1L &gt; 1UL 第一个操作数被提升为 unsigned long

在前面的例子中,表达式的值是1-1L &lt; 1L。在后一种情况下,nunsigned long 类型的最大值(在您的情况下为n+1 == 2^32),通过重复添加或减去n+1-1L 提升为无符号类型,这会产生大量数字(即@ 987654348@),因此整个表达式的值也是1int 类型)。

【讨论】:

    【解决方案2】:

    嗯……答案更长(详细),答案更短(近似)……


    ...从更长的答案开始:

    标准(C99,6.3.1)规定了一个整数转换等级,当两个整数操作数有不同的等级时,一个会向上转换。 rank 与整数的大小有关;较大的尺寸比较小的尺寸具有更高的排名。在大小相同的地方,排名可能会有所不同——但这并不重要。但是,相同大小的有符号整数和无符号整数具有相同的rank

    值得记住的是,签名intunsigned int 根据定义具有相同的大小。它们是标准所称的对应整数类型(C99,6.2.5)的示例。每个有符号整数类型都有一个对应的无符号整数类型,反之亦然——_Bool 除外,它没有符号(并且,FWIW,具有最低的 rank)。如上,对应有符号和无符号类型具有相同的rank

    C 非常喜欢intunsigned int。它会将小于 rank 的整数提升到 int(或提升到 unsigned int,如果提升的整数是无符号的并且大小与 int 相同)。这称为整数提升(它是所有可能的转换的子集)。对于许多运算符,C 将在执行任何其他操作之前对两个操作数执行整数提升。它还将对printf() 之类的参数执行整数提升

    现在我们了解所谓的通常的算术转换(C99,6.3.1.8),对于两个整数操作数来说:

    1. 两个参数都根据需要提升,因此至少会是intunsigned int

    2. 如果他们现在是同一类型(类型,而不是排名):

      • 如果两种类型都有符号,或者两种类型都没有符号,则较低的 rank 转换为较高的。

        这很简单,显然没有损失签名。

      ...否则,对于不同类型、不同符号,大小很重要:

      • 如果有符号操作数具有更大的大小(因此 rank),则无符号操作数将转换为有符号操作数的类型。

        这也很简单,无符号值可以无损失地表示为有符号类型。

      • 如果无符号操作数具有更大的大小(因此 rank),则将有符号操作数转换为无符号操作数的类型。

        因此,对于(例如)unsigned long longint(假设它们的大小不同),int 被向上转换,方法是向其添加 ULLONG_MAX + 1

        这不是那么简单,签名性丢失了。

      ...否则,对于不同类型、不同符号、相同大小,结果将是两个具有当前大小的无符号操作数。 rank 决定操作数的类型:

      • 如果操作数具有相同的rank,则将有符号的操作数转换为无符号操作数的类型。

        当您有 intunsigned int 操作数时会发生这种情况,int 被转换(通过添加 UINT_MAX + 1 到它)。

      • 如果无符号操作数具有更大的rank,则将有符号操作数转换为无符号操作数的类型(如上)。

      • 否则,有符号操作数的rank较大,两个操作数都转换为有符号操作数类型对应的无符号类型(即转为rank较大的无符号类型 em>)。

    这一切看起来都非常复杂 :-( 但是,请记住,整数的大小是其 rank 的主要组成部分——是什么让处理相同大小的整数看起来很复杂,但不同的符号和类型。

    (对于挑剔的人:我已经掩盖了标准中的皱纹,它允许整数表示包含“填充”位——这是非常奇特的——因此,在需要时,对于大小读取宽度。)


    ...但它的本质是,简而言之:

    升级到intunsigned int后:

    1. 如果操作数的大小不同,则较小的向上转换,如果较大的无符号但较小的无符号,则较小的将失去其符号性。

    2. 如果操作数的大小相同,但符号不同,则将有符号的操作数转换为无符号的,失去符号的。

    转换为无符号(失去有符号性)需要加上 MAX+1,其中 MAX 是要转换为的无符号类型的最大值。

    因此:-1L &gt; 0UL...确实是-1L == ULONG_MAX

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-24
      • 2018-04-07
      • 2020-09-03
      • 2017-05-03
      • 2017-03-29
      • 1970-01-01
      • 2012-12-25
      • 2017-03-28
      相关资源
      最近更新 更多