【问题标题】:What is the value of ~0 in C?C中〜0的值是多少?
【发布时间】:2014-06-04 12:00:09
【问题描述】:

我想获取INT_MININT_MAX 的值。我试过~0~0 >> 1,因为最左边的位是符号位,但我得到了-1

很困惑,为什么~0 不是0xffffffff~0 >> 1 不是0x7fffffff

【问题讨论】:

  • ~0 为 0xffffffff,即 -1 的二进制表示。如果我没记错的话,Minint 将是 0x80000000。
  • @MattiasÅslund 只有当 int 为 32 位并使用 2 的补码时,它才是 0x80000000
  • 如果您不了解有符号和无符号移位的概念,请阅读此right shift on negative number 注意这是 Java 链接,两种语言中运算符的行为不同但仍然有两个图表清楚显示什么是有符号和无符号的右移..
  • User3505816 我忘了在我的答案中添加 ~0 本身的实现定义阅读 Calculating Ranges of Data Types in C

标签: c bit-manipulation


【解决方案1】:

用途:

~0U >> 1

后缀“U”表示无符号移位行为。

所以,很困惑为什么不是~0 原来是0xffffffff

看,0 用四个字节表示:

BIT NUMBER    31                                     0
               ▼                                     ▼
number bits    0000 0000 0000 0000 0000 0000 0000 0000 
               ▲                                     ▲ 
              MSB                                   LSB



LSB - Least Significant Bit (numbered 0)
MSB - Most  Significant Bit (numbered 31) 

现在~ 是按位非运算符,然后将0 中的所有位翻转为:

BIT NUMBER    31                                     0
               ▼                                     ▼
number bits    1111 1111 1111 1111 1111 1111 1111 1111
               ▲                                     ▲ 
              MSB                                   LSB

由于 MSB = 1,此表示被视为负数,其大小是使用 2' 补码计算得出的,即 -1

如何?

1 是什么?它是:

number bits    0000 0000 0000 0000 0000 0000 0000 0001 
               ▲                                     ▲ 
              MSB                                   LSB

1 的补码

number bits    1111 1111 1111 1111 1111 1111 1111 1110
               ▲                                     ▲ 
              MSB                                   LSB

2'补码?在一个补码中添加1,即:

number bits    1111 1111 1111 1111 1111 1111 1111 1111
               ▲                                     ▲ 
              MSB                                   LSB

这和你得到 ~0 时一样吗?这就是为什么你得到-1 输出。

现在 >> 移位运算符?

在 C 的大多数实现中 >> 运算符被定义为算术右移,它保留符号位 MSB。所以~0 >> 1 注意到但-1 保持不变。

6.5.7 [Bitwise shift operators]

5 E1 >> E2 的结果是E1 右移E2 位位置。如果E1 具有无符号类型或E1 具有带符号类型和非负值,则结果的值是E1 / 2E2 商的整数部分。如果E1 具有带符号类型和负值,则结果值是实现定义的。

您的要求是所谓的无符号右移>>,您需要的行为可以使用无符号数找到,这就是我将U 后缀为0U 的原因。

如何打印INT_MIN和INT_MAX?

因为在 C 中打印 INT_MIN 和 INT_MAX 有点棘手(因为设置 MSB 和位溢出的未定义和实现行为),所以我编写了如下代码:

#include <stdio.h>
#include<limits.h> /* include for CHAR_BIT */
int main(){
  int my_int_min = 1U << ((sizeof(int) * CHAR_BIT) - 1);
  int my_int_max = ~0U >> 1;
  printf("INT_MIN  = %d\n", my_int_min);
  printf("INT_MAX  = %d\n", my_int_max);
  return 0;
}

看到它执行@codepad,它的输出是:

INT_MIN  = -2147483648
INT_MAX  = 2147483647 

这段代码是如何工作的?

注意 32 位数字范围是 [-2147483648, 2147483647],等于 [-2<sup>31</sup>, 2<sup>31</sup> -1 ]

INT_MIN: -231 == -2147483648 是:

    1000 0000 0000 0000 0000 0000 0000 0000 
    ▲                                     ▲ 
    MSB                                   LSB

在表达式1U &lt;&lt; ((sizeof(int) * CHAR_BIT) - 1) 中,我将LSB(即1)的第一位移到MSB 的最左侧,并且因为在C 中,设置有符号位是未定义的行为当操作数是单数类型时所以我用了一个未签名的1U。

6.5.7 [Bitwise shift operators]

E1 &lt;&lt; E2 的结果是 E1 左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1 × 2E2 ,比结果类型中可表示的最大值多模一减少。 如果 E1 有带符号类型和非负值,并且 E1 × 2E2 在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。

要注意的另一点是我使用了 CHAR_BIT 一个在limits.h 中定义的标准宏,它告诉C 实现中一个字符中的位数(请记住:字符始终是一个字节大小,但一个字节中的位数可以是不同系统上的不同并不总是保证为 8)。

INT_MAX: 231 -1 == 2147483647

    0111 1111 1111 1111 1111 1111 1111 1111
    ▲                                     ▲ 
    MSB                                   LSB

【讨论】:

  • 你的答案很清楚,但我仍然有疑问:我听说一个 INT 并不总是四个字节,但是当一个字节不包含八个时,你能更具体地描述一下吗位?
  • @user3505816 实际上你问了两个疑问 (1) INT 可能不是 4 个字节,它可以是 2 个字节或 8 个字节,这取决于系统的体系结构和编译器要知道你的系统使用的 int 的大小(以字节为单位)此代码printf("%zu \n", sizeof (int)); 并且如果您特别需要 4 字节整数使用数据类型 int32_t 作为 int32_t i; 同样对于 2 字节使用 int16_t ....(2) 一个字节可能不是由 8 位组成,这就是为什么 CHAR_BIT存在读取What is CHAR_BIT?
  • @user3505816 阅读 Jonathan Leffler 的 answer
【解决方案2】:

0 的类型为 int~0~0 &gt;&gt; 1 也是因为 int type promotion

~0 在其位模式中全为 1,在 2 的补码中为 -1,这是大多数现代实现的默认表示。

C 中的右移是implementation defined。但是大多数实现将&gt;&gt;定义为arithmetic shift,当类型为有符号时,在类型为无符号时进行逻辑移位

由于~0int,这是一个有符号类型,~0 &gt;&gt; 1 将是一个算术右移。因此该值被符号扩展,导致该值全为 1

你需要做unsigned(~0) &gt;&gt; 1或~0U

没有办法获得INT_MININT_MAX 可移植,因为在C 中除了trap representationspadding bits 之外还有3 种不同的有符号类型实现。这就是为什么标准库总是直接用值定义INT_MININT_MAX

【讨论】:

  • unsigned(~0)~0u 如果非 2 的补码是不同的; ~0u 是预期版本。
【解决方案3】:

~0 -1。您可能遇到的每个 C 实现都对有符号整数使用二进制补码,因此 0xffffffff-1(假设为 32 位整数)。 ~0 &gt;&gt; 1 相当于将-1 除以2;因为我们在做整数运算,所以结果是-1

【讨论】:

  • ~0 &gt;&gt; 1 未指定,因为负整数上的所有位移。如果您仍然得到 -1,那是因为编译器发出的程序集要求在位移时进行符号扩展。
  • 是的。但是编译器更有可能为~0&gt;&gt;1 输出-1,而不是其他任何东西。即使按照标准,当他们遇到类似的情况时,技术上允许他们将硬盘驱动器的全部内容发布到 stackoverflow。
  • 当然没有人深入到 UB(这不是 UB,而是特定于实现的行为)。关键是通常编译器更倾向于执行当前平台上的右移汇编指令所做的任何事情。如果算术和按位移位都可用,则归结为实现的选择。
【解决方案4】:

所有位集int 的值取决于您的平台对int 的符号表示。这就是为什么发明了宏 INT_MININT_MAX 的原因,没有办法以可移植的方式计算这些值。

【讨论】:

    【解决方案5】:

    根据wikipedia 文章,C 通常实现算术移位。这意味着当您右移数量 0xffffffff 时,1 的最左侧位(符号位)将被保留,如您所见。

    但是,维基百科也提到了以下内容,因此如果您使用无符号类型,您将得到一个逻辑移位(0x7fffffff 的结果)。

    C 和 C++ 中的 >> 运算符不一定是算术移位。 如果与有符号整数一起使用,通常它只是算术移位 在其左侧键入。如果它用于无符号整数类型 相反,这将是一个合乎逻辑的转变。

    【讨论】:

      【解决方案6】:

      数字存储在 2 的补码中,因此 ~00XFFFFFFFF 即 -1。
      所以FFFFFFF(1111) &gt;&gt;1 给出(1111)FFFFFFF = 0XFFFFFFFF = -1

      当移动unsigned 值时,C 中的&gt;&gt; 运算符是logical shift。移动 signed 值时,&gt;&gt; 运算符是 arithmetic shift
      所以,~0U &gt;&gt; 1 给出(1111)FFFFFFF = 0XFFFFFFFF

      【讨论】:

        【解决方案7】:

        在 32 位系统上,00x00000000~bitwise not operator,它将每个0 位转换为1,反之亦然。因此,~0 (~0x00000000) 给出了0xffffffff

        这又被解释为Two's complement中的-1

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-03-29
          • 1970-01-01
          • 1970-01-01
          • 2016-11-22
          • 2013-08-05
          • 1970-01-01
          相关资源
          最近更新 更多