C - 当它始终编码为二进制的 2 的补码时,负字符中的带符号位在哪里——(-128)?

C - where is the signed bit in the negative char—(-128)—when it's always encoded as the 2's complement of its binary?

我无法调和上述两个事实。

如果我们在这里查看 -128 的示例,则在对其进行编码时会执行以下步骤

  1. 找出 128 的二进制等价物:10000000
  2. 取 1 的补码:01111111
  3. 1 与 1 的补码相加得到 2 的补码:10000000

我的问题是:负整数的 sign bit 在哪里?换句话说,我想了解 10000000 如何解码为 -128 而不是 -0

  1. 如果最左边的 1 (MSB) 用于编码 -128 的负号,那不就留下了 7 位二进制 0000000,其十进制等效值为 0(而不是 -128)吗?
  2. 或者计算机(当然是比喻性的)——当对存储在某个内存位置的负整数执行计算时——以相反的顺序执行上面的步骤 1-3 以解码 1 字节 char 的值每当它看到 1 作为最高有效位(而不是 0)时?
  3. 或者MSB是在-128中对第8位(2^7)的符号和位进行编码。

我已经看到了 this 问题,但我的不同,因为我非常清楚 +128 不能存储在 1 字节中,因为它的签名二进制文件会转换为 0100000000 这需要9 位。

确切的布局取决于在机器上 (CPU),但在 C 上。编译器是为一台特定的机器,并知道如何指示它正确处理它。

Where is the sign bit for a negative integer?

Door #3: "或者MSB是在-128

中同时编码第8位(2^7)的符号和位

使用 8 位有符号 char,编码为 2 的补码 ,M 为 7 并且...

the sign bit has a value of -(2M) (C17dr § 6.2.6.2 2)

10000000 是 -128 + 0 * 64 + 0 * 32 + 0 * 16 + 0 * 8 + 0 * 4 + 0 * 2 + 0 * 1 --> -128

虽然二进制补码是迄今为止表示有符号整数值的最常见方式,但它并不是唯一的方式,并且 C 不需要二进制补码表示。

请注意,您不能在 any 表示中仅用 8 位表示 signed 128 - 您可以表示范围 [-127..127] 的补码或符号大小,或范围 [-128...127] 的补码。所以 根据定义 你需要超过 8 位来表示带符号的 128:

        two’s      ones’       sign-magnitude

 125    01111101   01111101    01111101
 126    01111110   01111110    01111110
 127    01111111   01111111    01111111
-128    10000000   n/a         n/a
-127    10000001   10000000    11111111
-126    10000010   10000001    11111110
-125    10000011   10000010    11111101

对于任何使用整数值的数据类型,如“int”、“short”、“byte”、“char”、“long”,除了“unsigned”,它们的值范围为:( -1 * 2(总位数 - 1)) 到 (2(总位数 - 1)) - 1。最左边的位用作符号位。

对于 char 数据类型,其大小为 1 个字节。的位是 8 位。 1 最左边的位用于符号,其余位用于值。所以我们得到的没有符号的值来自

0 到 127 即 (00000000)2 到 (01111111)2

当它与符号位一起时,它的范围是

-128 到 -1 即 (10000000)2 到 (11111111)2.

If we look at the example of -128 here, the following steps are taken while storing it…

存储时不是,calculating/converting时是。当编译器在源代码中处理 -128 时,或者当我们在纸上看到它并使用它时,我们会做任何我们想做的计算。我们可以使用位、数字或纸上的标记来实现我们想要的任何东西。当我们产生最终答案时,然后 最终答案中的位具有它们的最终含义。中间步骤不必以相同的方式使用位。

给定“128”,我们计算出这是纯二进制的 10000000(无符号)。那么我们可以计算出它的补码表示,将01111111的位补1(还是纯二进制,无符号),得到10000000。那么这些相同的位就是补码表示。

字节被解释时,包括用于算术运算或将二进制补码表示形式转换为十进制时,高位将被解释为符号位。但是,同样,我们不需要在整个计算过程中以相同的方式使用位。我们可以取 10000000,看到高位被设置为告诉我们这个数是负数,然后像以前一样取它的二进制补码:将这些位补码为 01111111 然后加一得到 10000000。现在我们有相同的位,但是它们是纯二进制数,没有符号。它们代表128,我们知道它是负数,因为我们之前观察到了原始符号位。

另请注意,signed char xunsigned char y 使用相同的位模式来表示不同的值。当x的位模式为11111111时,表示-1。当 y 的位模式为 11111111 时,它表示 255。为了使其工作,编译器将对 x 的操作与 y 的操作使用不同的指令。使用有符号类型与使用无符号类型有不同的说明。 (其中许多在很大程度上重叠;加法和减法通常使用相同的指令执行,但标志结果的解释不同,以检测溢出和其他情况。)

此外,对于这个 single-byte 示例,编译器通常不会将其作为 char 使用。在源文本中,128 是一个 int 常量。在内部,编译器可能会将 128 转换为 32 位 int,然后将其取反以生成 −128,位为 11111111111111111111111110000000,然后将其存储在带符号的 char 中,它取低八位,10000000。(这可能因编译器而异。)

有趣的是,这个边界问题确实影响了 -2147483648 的类型。考虑使用 32 位 int 和 64 位 long 的 C 实现。 −2,147,483,648 可表示为 32 位 int,但在 C 语法中,-2147483648 不是常量,而是 -2147483648 的组合。而且,由于 2,147,483,648 无法用 32 位表示,因此它是 long 常量。所以-2147483648的类型是long。您可以通过以下方式验证:

printf("%zu %zu\n", sizeof -2147483647, sizeof -2147483648);

它将在 C 实现中用 32 位打印“4 8”int

(这引发了如何定义 INT_MIN 的问题。它的值必须为 −2,147,483,648,但类型必须为 int。)