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(有符号)。 在第二种情况下,它必须打印(2 ^ 31-1),但它打印255和255。 为什么在两种情况下都打印255。 告诉我,如果我的假设是错误的,并给我真正的解释。
答案 0 :(得分:2)
你的假设是错误的;这个角色将会翻身#34;到255,然后填充到整数的大小。假设一个32位整数:
11111111
将填充到:
00000000 00000000 00000000 11111111
答案 1 :(得分:0)
unsigned char
从0-255
开始,因此负数-1
将打印255
-2
将打印254
等等...... < / p>
signed char
从-128 to +127
开始运行,因此对于同一printf()
,您获得-1,而unsigned char
在对char进行赋值后,其余的整数值将被填充,因此您对2^31
的假设是错误的。
负数用2的补码表示(取决于实现)
所以
1 = 0000 0001
所以为了得到-1
我们做
----------------------------------------
2's complement = 1111 1111 = (255) |
-----------------------------------------
答案 2 :(得分:0)
编译器不知道printf
中的额外参数应该是什么类型,因为唯一指定它的东西应该被视为4字节{{1格式字符串,在编译时无关紧要。
幕后实际发生的是被调用者(int
)接收到每个参数的指针,然后转换为适当的类型。
与此大致相同的结果:
printf
由于你可能在一个小端CPU上运行,4字节char a = -1;
int * p = (int*)&a; // BAD CAST
int numberToPrint = *p; // Accesses 3 extra bytes from somewhere on the stack
0x12345678在内存中安排为int
如果| 0x78 | 0x56 | 0x34 | 0x12 |
之后的堆栈上的3个字节都是a
(它们可能是由于堆栈对齐,但它不是保证),内存看起来像这样:
0x00
评估为&a: | 0xFF |
(int*)&a: | 0xFF | 0x00 | 0x00 | 0x00 |
。
答案 3 :(得分:0)
直到a
的表示,你是对的。但是,%d
函数的%u
和printf()
次转换都以int
为参数。也就是说,您的代码与您编写的代码相同
int main() {
unsigned char a = -1;
printf("%d", (int)a);
printf("%u", (int)a);
}
当您将-1
分配给a
时,您丢失了曾经是有符号值的信息,a
的逻辑值为255
。现在,当您将unsigned char
转换为int
时,编译器会保留a
的逻辑值,代码将打印255
。
答案 4 :(得分:0)
打印255,只是因为这是ISO / IEC9899的目的
H.2.2整数类型
1带符号的C整数类型为int,long int,long long int,以及相应的 无符号类型与LIA-1兼容。如果实现添加了对。的支持 LIA-1例外值''integer_overflow''和''undefined'',然后是那些类型 LIA-1符合类型。在LIA-1意义上,C的无符号整数类型是“模数” 在溢出或越界结果静默包装。定义的实现 有符号整数类型也是模数,不需要检测整数溢出,在这种情况下, 只需要检测整数除零。
如果给出了这一点,那么印刷255绝对是LIA-1所期望的。
否则,如果您的实现不支持C99的LIA-1附件部分,那么它只是未定义的行为。