char c=128问题

##一、文章来由

同属计算机储存,补码问题的延生~~

##二、解密

答案输出是-128,为什么呢?

char型变量c中存储的是128的补码:10000000。上一篇文章已经说到,对于计算机来说,存储的都是数据的补码。

赋值给char型,debug模式下反汇编如下:

char c = 128;
00C6446E  mov         byte ptr [c],80h  

	char cc = c;
00C64472  mov         al,byte ptr [c]  
00C64475  mov         byte ptr [cc],al  
	unsigned int ui = c;
00C64478  movsx       eax,byte ptr [c]  
00C6447C  mov         dword ptr [ui],eax  
	int i = c;
00C6447F  movsx       eax,byte ptr [c]  
00C64483  mov         dword ptr [i],eax  
	double d = c;
00C64486  movsx       eax,byte ptr [c]  
00C6448A  cvtsi2sd    xmm0,eax  
00C6448E  movsd       mmword ptr [d],xmm0
00C64478  movsx       eax,byte ptr [c]

使eax寄存器扩展成为 0xFFFFFF80,扩展在这一步~~

这里写图片描述

而且

00C64475  mov         byte ptr [cc],al   //char cc = c;
00C6447C  mov         dword ptr [ui],eax //unsigned int ui = c;
00C64483  mov         dword ptr [i],eax  //int i = c;
00C6448E  movsd       mmword ptr [d],xmm0 //double d = c;

进一步将寄存器存到内存中

对于计算机来说

char c = -128; signed char c = 0x80; 以上两句是一回事。

如下几种情况:

char c=128; printf(“%u\n”, c); //按%u输出,结果为 4294967168 printf(“%d\n”, c); //按%d输出,结果为 -128 printf(“%c\n”, c);//按%c输出,结果为空

原因:

c 是一个字符的长度,但当它被传送到printf函数的参数时,是将c按照int来扩张传给printf的。 128 即为二进制的 1000 0000,16进制的0x80, 当它扩展为 int时,由于int可能是4个字节,所以会进行符号扩展。由于 128最高为是1,为负数,扩展为int的-128为11111111 111111 111111 1000000,即0xffffff80

而0xffffff80按照有符号的int来说,就是-128。 而按照无符号的int来说,就是十进制的 4294967168。

    unsigned char uc=128;
    char c=128;
    printf("%d\n",uc);      //128
    printf("%d\n",c);       //-128
    printf("%u\n",uc);      //128
    printf("%u\n",c);       //4294967168
    printf("%08x\n",uc);    //0x00000080
    printf("%x\n",c);       //0xffffff80

##三、数据扩展

###1、短数据类型扩展为长数据类型

(1)要扩展的短数据类型为有符号数

进行符号扩展,即短数据类型的符号位填充到长数据类型的高字节位(即比短数据类型多出的那一部分),保证扩展后的数值大小不变

例1:char x=10001001b; short y=x; 则y的值应为11111111 10001001b;

例2:char x=00001001b; short y=x; 则y的值应为00000000 00001001b;

(2)要扩展的短数据类型为无符号数*

进行零扩展,即用零来填充长数据类型的高字节位

例1:unsigned char x=10001001b; short y=x; 则y的值应为00000000 10001001b;

例2:unsigned char x=00001001b; short y=x; 则y的值应为00000000 00001001b;

###2、长数据类型缩减为短数据类型

如果长数据类型的高字节全为1或全为0,则会直接截取低字节赋给短数据类型;如果长数据类型的高字节不全为1或不全为0,则转换就会发生错误。(这个还不确定)

事实上下面代码会输出 ‘0’

	int a = 0xAA30;
	char c = a;
	printf("%c",c);

###3、同一长度的数据类型中有符号数与无符号数的相互转化

直接将内存中的数据赋给要转化的类型,数值大小则会发生变化,因为以不同类型解释同一段内存数据会得到不同的数值。比如一个字节中存放的数据是11111111,以unsigned char来解释就是255,以char来解释就是-128.

根据以上规则,可以得出当char c 是一个有符号的字符变量,其内存中存储的是1000 0000,但当它被传送到printf函数的参数时,是将c按照int来进行宽度扩展后再传给printf的。

128的补码是 1000 0000,16进制是0x80, 当它扩展为 int时,由于int是4个字节,需要进行短数据类型扩展到长数据类型。由于内存中存放的是10000000,以char型来解释的话第一位为符号位,表示负数,进行符号扩展为int后,int型变量中存储的数据是:11111111 11111111 11111111 1000000,即0xffffff80。以int来解释的这四个字节的数据,其值就是-128,以unsigned int来解释的话,就是232−1−127=4294967168。

##四、为什么会在前面补1?

开始的char c 是 1000 0000b,后来变成 11111111 11111111 11111111 1000000b,为什么要在前面补1呢?

或者举例,char c 是 1000 0001b,后来变成 11111111 11111111 11111111 1000001b

因为要使 -2^31+2^30+…+2^7+0+1 = -2^7+0+1 也就是 -2^31+2^30+…+2^8 = -22^7 也就是 -2^31+2^30+…+2^8+22^7 = 0

-2^31+2^30+…+2^8+2*2^7 = -2^31+(2^31-1)-(2^6+2^5+…+1)+2^7 = 0