硬件如何知道一个变量是正数还是负数?

如果这个问题太基本,我很抱歉......我只是没有在任何地方找到答案。

假设我像这样声明了一个 C 变量:

unsigned int var = 241;

在这种情况下,var 是无符号的,所以我的目的是让它具有十进制值 241。

或者我可以这样声明:

signed int var = -15;

在这种情况下,我将其声明为有符号整数,因此根据我的理解,它应该具有十进制值 -15。

但是,这两次,我都假设 var 将在内存(硬件)中声明如下:1111 0001。

那么处理器如何知道在硬件中的最低级别我打算将其声明为 241 还是 -15?我知道用于表示负数等的二进制补码表示法,但是,我假设在硬件中,处理器只能看到一系列 1 和 0,然后通过切换某些 IC 的状态对其进行一些操作。处理器如何知道是解释标准二进制(无符号)还是 2 的补码(有符号)中的位序列?

还有另一个有点不相关的问题:

  1. 在 CI 中可以这样做:

    无符号整数 var = -15; printf("变量是:%d", var); 这将按预期打印 -15。为什么,当我这样做时:

有符号 int var = 0xF1; //或 0b11110001 printf("The var is: %d ", var);

我得到 241 而不是 -15?既然我声明它是有符号的并且在二进制补码中 0xF1 是 -15 为什么我得到的值 241 相当于标准二进制中的 0xF1 ?

  1. 为什么编译器让我做这样的事情: unsigned int var = -15;

它不应该抛出一个错误,告诉我我不能将负值分配给我声明为无符号的变量吗?

谢谢你,我为我的许多可能是基本的问题道歉,我不知道的太多了:D。

回答

硬件不知道。
编译器知道。
编译器知道,因为你在这里说过signed int var = -15;,“亲爱的编译器,这是一个可以为负的变量,我将它初始化为负值。”
在这里你说的是不同的unsigned int var = 241;,“亲爱的编译器,这是一个不能为负的变量,我将它初始化为正值。”

编译器将记住这一点,以便您以后对变量及其值进行任何操作。编译器会将所有相应的代码转换为机器语言中的那组指令,这将导致硬件做出相应的行为。所以硬件最终会做一些适合消极或不适合的事情;不是因为知道,而是因为没有选择。

“相应指令”的一个有趣方面(正如 Peter Cordes 在下面的评论中所指出的)是,对于负值的 2 补码表示的特殊(但非常广泛使用)情况,指令实际上对于两者是相同的(这是 2-complement 的一个重要优势)。

  • The beauty of 2's complement is that addition / subtraction are the same binary operation as for unsigned; same for non-widening multiply. Only one's complement or sign/magnitude machines need different instructions for signed/unsigned basic math ops. (2's complement machines just need different compare and/or branch instructions).

回答

如果两个值是char(有符号或无符号),那么它们的内部表示(8 位模式)在内存或寄存器中将是相同的。唯一的区别在于编译器在处理这些值时发出的指令。例如,如果这些值存储在声明signed或in 的变量unsignedC,则这些值之间的比较将使编译器在汇编级别生成有符号无符号的特定比较指令。

但是在您的示例中,您使用ints。假设在您的平台上这些ints 使用四个字节,那么当涉及到它们的 32 位模式时,您提供的两个常量并不相同。较高位考虑值的符号并传播以填充 0 或 1 到 32 位(参见0f下面的序列)。

请注意,unsigned int如果您使用正确的编译器标志(-Wconversion例如),为 an 分配负值会在编译时产生警告。在下面的评论中,@PeterCordes 提醒我们这样的赋值在 C 中是合法的,并且在某些情况下很有用;使用(或不)编译器标志来检测(或不)这种情况只是个人选择的问题。但是,赋值-15U而不是-15明确将常量视为无符号的意图(尽管有减号),并且不会触发警告。

int i1=-15;
int i2=0xF1;
int i3=241;
printf("%.8x %dn", i1, i1); // fffffff1 -15
printf("%.8x %dn", i2, i2); // 000000f1 241
printf("%.8x %dn", i3, i3); // 000000f1 241
unsigned int u1=-15; // warning: unsigned conversion from ‘int’ to ‘unsigned int’ changes value from ‘-15’ to ‘4294967281’
unsigned int u2=0xF1;
unsigned int u3=241;
printf("%.8x %un", u1, u1); // fffffff1 4294967281
printf("%.8x %un", u2, u2); // 000000f1 241
printf("%.8x %un", u3, u3); // 000000f1 241

  • I guess that conversion warning is from MSVC? GCC and clang don't warn about it because it's perfectly legal C, and `-16U` or `-1U` are useful ways some bit-patterns, e.g. `x & -16U` rounds down to a multiple of 16. So it would be annoying to get warnings about it in contexts like that. https://godbolt.org/z/vM64df. But MSVC's `-Wall` enables a bunch of warnings including ones that are often spurious / false-positive, so that's fine and potentially useful and a good fit for that. (I was hoping GCC or clang would have a warning for it at `-Wpedantic` or something, but I didn't find one.)
  • Note that in formal C terms, `-15U` isn't ever a negative number. As MSVC warns (https://godbolt.org/z/nf9x8M): "*C4146: unary minus operator applied to unsigned type, result still unsigned*". So it's exactly identical to `(0U - 15U)`. C numeric-literals don't include minus signs; that's why `-0x80000000` has type `unsigned` in 32-bit-int C implementations: 0x80000000 doesn't fit in a signed 32-bit int, so it promotes to unsigned, *then* unary `-` is applied. https://godbolt.org/z/eW7bsx. This means you normally need a cast back to signed or something like that to define `INT_MIN`.

以上是硬件如何知道一个变量是正数还是负数?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>