c语言中单字节数据转换为多字节数据需要补符号位

我们在该文中分析了uchar/char的范围及越限情况。

期间有个现象在这里分析以下:

我们发现,uchar以十六进制的形式输出是FF,但是char类型以十六进制输出的是ff ff ff ff

    uchar ch1 = 0xFF;

    char ch2 = 0xFF;

    printf("%d,%d\n%X,%x\n",ch1,ch2,ch1,ch2);
    printf("%d",sizeof(int));

分析:编译器是32位的,所以是 ff ff ff ff,共32位, 4个字节。又因为,对于char类型,ff(1111 1111),首位是1,故为负数,所以,拓展成4个字节后,前面全是1

具体可参考本文

补符号位原则:

如果数据类型为有符号的(比如char类型),且数据最高位是1,则转换成多字节数时,高位全都补1,即十六进制数都是F。

包括数据为负数时,比如 int = -32768;

如果数据类型为无符号的(比如uchar类型)高位全都补0,即十六进制数都是0,默认是不写的。

此外,char与多字节的位运算(即,强转成多字节时),以及如下的位移运算也算牵扯到补符号位的问题。

数据存储在计算机中的是二进制,如果是正数存的是原码(也可以理解为补码,因为正数的原码和补码相同),如果是负数存的是补码,归纳为,

1)数据存在计算机的都是补码

2)整型为十六进制数,则表示的就是补码;//char ch = 0xaa; 

3)整型数据为十进制数,则需要先转换成二进制补码的形式(十六进制);

4)同一个数存储在相同位数的数据类型中,其存储的二进制(十六进制)是完全一样的,不会发生丢位,最终转换为十进制数,或者扩展为多字节数是多少,是由我们要解析的形式决定的。

// char ch1=-1;   uchar ch2 = -1; 二者存储的二进制都是 FF;

//printf("%x,%x",ch1,ch2);//输出的是FF FF FF FF;    FF

5)数据转换过程: uchar ch =-1; printf("%x,%d,%u",ch,ch,ch);

  • 在32位机上编译: -1默认的是int型,转换为二进制:FF FF FF FF
  • uchar位一个字节,故截取低八位,为FF;
  • 按照补符号位原则,根据%x,%d,%u的规则,解析输出;

或者使用如下的思路:

6)printf %d相当于数据类型转换为int  %u 相当于转换为 unsigned int 

7)初始化的整型常量,默认是 int类型的。

举例:

   //十进制数显示问题
uchar ch1 =-1;
printf("%x,%d,%u\n",ch1,ch1,ch1);
char ch2 =-1;
printf("%x,%d,%u\n",ch2,ch2,ch2);
printf("*****************\n");

//十六进制数显示问题
uchar ch3 =0xAA;
printf("%x,%d,%u\n",ch3,ch3,ch3);
char ch4 =0xAA;
printf("%x,%d,%u\n",ch4,ch4,ch4);
printf("*****************\n");

//数据类型强转后数据显示问题
uchar ch5 = -1;
printf("%x,%d,%u\n",ch5,ch5,ch5);
char ch6 = -1;
printf("%x,%d,%u\n",(uchar)ch6,(uchar)ch6,(uchar)ch6);
printf("*****************\n");

//不同数据类型相互赋值后的显示问题
uchar ch7 = -1;
char ch8 = -1;

char ch9 = ch7;
uchar ch10 = ch8;
int ch11 = ch7;        //原数据类型无符号,值不受影响
uint ch12= ch7;        //原数据类型无符号,值不受影响
int ch13= ch8;         //短字节扩展为长字节需要补符号位
uint ch14 = ch8;       //短字节扩展为长字节需要补符号位

printf("%x,%d,%u\n",ch7,ch7,ch7);
printf("%x,%d,%u\n",ch8,ch8,ch8);
printf("%x,%d,%u\n",ch9,ch9,ch9);
printf("%x,%d,%u\n",ch10,ch10,ch10);
printf("%x,%d,%u\n",ch11,ch11,ch11);
printf("%x,%d,%u\n",ch12,ch12,ch12);
printf("%x,%d,%u\n",ch13,ch13,ch13);
printf("%x,%d,%u\n",ch14,ch14,ch14);

//从下图结果可以看到值为-1时,无论数据类型是uchar还是char类型, 其十六进制数都是FF(不考虑符号位),这是因为,-1这个常量默认的数据类型是int 

//判断数据转换,将原数据转换成补码,此时再理解。

    short si = -32768;
    unsigned short usi=si;

    int i = si;
    unsigned int ui=usi;

    unsigned int ui2=-32768;//本身就是int/uint格式,不需要补位
    unsigned int ui3=0xAA;
    printf("short:          decimal:%d  Hexadecimal:%x\n",si,si);//扩充位数
    printf("unsigned short: decimal:%d   Hexadecimal:%x\n",usi,usi);//扩充位数
    printf("int:            decimal:%d  Hexadecimal:%x\n",i,i);//没有扩充位数
    printf("unsigned int:   decimal:%d   Hexadecimal:%x\n",ui,ui);//没有扩充位数
    printf("unsigned int:   decimal:%d  decimal:%u Hexadecimal:%x\n",ui2,ui2,ui2);//没有扩充位数
    printf("unsigned int:   decimal:%d   Hexadecimal:%x\n",ui3,ui3);//没有扩充位数

 unsigned int ui2=-32768;//本身就是int/uint格式,不需要补位

printf("unsigned int:   decimal:%d  decimal:%u Hexadecimal:%x\n",ui2,ui2,ui2);//没有扩充位数

注意:当不需要补符号位时(如上述情况),且数值不超过uint的最大值,也没有超过int的最小值时,是可以正常显示的!!!!

    short svalue= -32769;
    printf("%d,%x\n",svalue,svalue);

规律:

使用单字节表示负数的补码与使用多字节表示的补码相比,多字节时,只要在多的位数补1即可。

例如:

1)未越限的情况

//-127的补码:
单字节://-128+1
1000 0000  + 1  = 1000 0001

2字节://取反+1
1000 0000 0111 1111  取反+1: 1111 1111 1000 0001

2)越限的情况

//-130的补码:
单字节://-128-2  //想象圆圈循环 逆时针转2个----》126(或者利用加上模的方法 -130 +256==126)
1000 0000  - 2  = 1000 0000 -  0000 0010   ==  0111 1110

2字节://取反+1
1000 0000 1000 0010  取反+1: 1111 1111 0111 1110

解释:无论是否越限,此负数使用单个字节足以表示出来,故如果使用多字节表示,高字节(除符号位)都是0,取反后都是1.

越限后的循环,实际上非越限情况下的截断。

//对于越限的情况,另一种思路是,

1)可以按照高减低加的原则,先转换成正常值,然后,再看当前的数据类型是有符号的,还是无符号的,扩展位数是否需要添加符号位。

2)都按照正常数据来理解,如果当前数据类型是有符号的char,要以有符号形式输出则不会有问题,如要以无符号形式输出则值会发生变化;

如果当前数据类型是无符号的uchar,无论是以无符号形式还是有符号形式输出,值都相同,没有问题。

总结:

原则1:、数据类型中,数据越限情况的分析,遵循高加低减的原则。

原则2:以上大部分的例子(printf输出),本质上都是不同数据类型赋值转换的分析,此时要注意:

1)原数据类型的与目标数据类型的位数是否发生了变化;--->注意补符号位。

2)原数据类型与目标数据类型符号是否发生变化;

3)原数据类型是否是有符号的的类型

4)原数据类型是否发生了越限,如果是unsigned int 类型发生了越限,针对int型是否越限(因为默认的数据类型是int),如果是非unsigned int  的其他类型,参考原则1.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值