为什么打印 255

why is it printing 255

int main()
{

     unsigned char a = -1;
     printf("%d",a);
     printf("%u",a);
}

当我执行了上面的程序后,我得到了 255 255 作为答案。

我们知道负数将存储在 2 的补码中。

因为它是 2 的补码,所以表示 1111 1111 -->2的补码。

但在上面我们正在打印 %d(int) 但整数是四个字节。

我的假设是,即使它是字符,我们也会强制编译器将其视为整数。 所以它在内部使用符号扩展概念。

1111 1111 1111 1111 1111 1111 1111 1111.

根据上面的表示,在第一种情况下它必须是 -1,因为它是 %d(signed)。 在第二种情况下,它必须打印 (2^31- 1) 但它打印的是 255 和 255。 为什么在这两种情况下都打印 255。 告诉我我的假设是否错误并给我真正的解释。

unsigned char0-255 运行所以负数 -1 将打印 255 -2 将打印 254 等等。 .

signed char-128 to +127 运行,所以对于相同的 printf() 得到 -1,而 unsigned char

则不是这种情况

一旦您对 char 进行赋值,其余整数值将被填充,因此您对 2^31 的假设是错误的。

负数使用 2 的补码表示(取决于实现)

所以

1 = 0000 0001

所以为了得到 -1 我们做

----------------------------------------
2's complement      = 1111 1111 = (255) |
-----------------------------------------

你的假设是错误的;字符将 "roll over" 到 255,然后填充到整数的大小。假设一个 32 位整数:

11111111

将被填充为:

00000000 00000000 00000000 11111111

编译器不知道 printf 中的额外参数应该 是什么类型,因为唯一指定它的东西应该被视为 4 字节int为格式化字符串,编译时无关

幕后实际发生的事情是被调用者 (printf) 收到指向每个参数的指针,然后转换为适当的类型。

与此大致相同的结果:

char a = -1;
int * p = (int*)&a; // BAD CAST

int numberToPrint = *p; // Accesses 3 extra bytes from somewhere on the stack

由于您可能 运行 在小端 CPU 上,因此 4 字节 int 0x12345678 在内存中的排列方式为 | 0x78 | 0x56 | 0x34 | 0x12 |

如果堆栈上a之后的3个字节都是0x00(它们可能是由于堆栈对齐,但不能保证),内存如下所示:

&a:       | 0xFF |
(int*)&a: | 0xFF | 0x00 | 0x00 | 0x00 |

计算结果为 *(int*)&a == 0x000000FF

根据 a 的表述,您是正确的。但是,printf() 函数的 %d%u 转换都将 int 作为参数。也就是说,你的代码和你写的一样

int main() {
     unsigned char a = -1;
     printf("%d", (int)a);
     printf("%u", (int)a);
}

在您将 -1 分配给 a 的那一刻,您丢失了它曾经是有符号值的信息,a 的逻辑值为 255 .现在,当您将 unsigned char 转换为 int 时,编译器保留 a 的逻辑值并且代码打印 255.

它正在打印 255,只是因为这是 ISO/IEC9899

的目的

H.2.2 Integer types

1 The signed C integer types int, long int, long long int, and the corresponding unsigned types are compatible with LIA−1. If an implementation adds support for the LIA−1 exceptional values ‘‘integer_overflow’’ and ‘‘undefined’’, then those types are LIA−1 conformant types. C’s unsigned integer types are ‘‘modulo’’ in the LIA−1 sense in that overflows or out-of-bounds results silently wrap. An implementation that defines signed integer types as also being modulo need not detect integer overflow, in which case, only integer divide-by-zero need be detected.

如果给出这个,打印 255 绝对是 LIA-1 所期望的。

否则,如果您的实现不支持 C99 的 LIA-1 附件部分,那么它只是未定义的行为。