char 变成int型后的符号位扩展

二进制负数:

原码就是原来的表示方法

反码是除符号位(最高位)外取反

补码=反码+1

1个字节它不管怎么样还是只能表示256个数,因为有符号所以我们就把它表示成范围:-128-127。它在计算机中是怎么储存的呢?可以这样理解,用最高位表示符号位,如果是0表示正数,如果是1表示负数,剩下的7位用来储存数的绝对值的话,能表示27个数的绝对值,再考虑正负两种情况,27*2还是256个数。首先定义0在计算机中储存为00000000,对于正数我们依然可以像无符号数那样换算,从00000001到01111111依次表示1到127。那么这些数对应的二进制码就是这些数的原码。到这里很多人就会想,那负数是不是从10000001到11111111依次表示-1到-127,那你发现没有,如果这样的话那么一共就只有255个数了,因为10000000的情况没有考虑在内。实际上,10000000在计算机中表示最小的负整数,就是这里的-128,而且实际上并不是从10000001到11111111依次表示-1到-127,而是刚好相反的,从10000001到11111111依次表示-127到-1。负整数在计算机中是以补码形式储存的,补码是怎么样表示的呢,这里还要引入另一个概念——反码,所谓反码就是把负数的原码(负数的原码和和它的绝对值所对应的原码相同,简单的说就是绝对值相同的数原码相同)各个位按位取反,是1就换成0,是0就换成1,如-1的原码是00000001,和1的原码相同,那么-1的反码就是11111110,而补码就是在反码的基础上加1,即-1的补码是11111110+1=11111111,因此我们可以算出-1在计算机中是按11111111储存的。总结一下,计算机储存有符号的整数时,是用该整数的补码进行储存的,0的原码、补码都是0,正数的原码、补码可以特殊理解为相同,负数的补码是它的反码加1。下面再多举几个例子,来帮助大家理解!

十进制 → 二进制  (怎么算?要是不知道看计算机基础的书去)

47   → 101111

有符号的整数    原码    反码    补码

  47      00101111  00101111  00101111(正数补码和原码、反码相同,不能从字面理解)

 -47      10101111  11010000  11010001(负数补码是在反码上加1)

///////////////////////////

c语言数据类型长度

C语言中,只是能够明确sizeof(short)<sizeof(long),sizeof(short)≤sizeof(int)≤sizeof(long)而已,至于的int具体是16位还是32位的,取决于平台和语言实现(编译器)。< span="">在VC++(x86)等32位环境中,int和long都表示32位有符号整数,范围是一样的。
    

//////////////////////////

代码示例:

static get_utili(constchar*p){int util;…while(isspace((int)*p))//跳过空格++p;util=(int)*p++;…}

现象&后果:

当传入的参数p指向的内容为0x9A、0XAB等内容(最高位为1)时,得到的int型变量util的值将会出错,因为char会进行符号扩展,使得0x9A(十进制的154)变成了-102。会造成程序运行时的数据处理错误。

Bug分析:

char符号扩展是与编译器相关的,但在x86平台上,对于任何主流的编译平台,char总是进行符号扩展的。上述代码在将char型的*p赋给int型变量util的时候,需要先进行char型到unsigned char型的转换,以避免按照char的最高位进行符号扩展。

上述出错代码的符号扩展过程如下:

因为要扩展的短数据类型为有符号数的-- char x=10011100b(即0x9A)

因而在int y=(int)x时--进行符号扩展,即短数据类型的符号位填充到长数据类型的高字节位(比短数据类型多出的那一部分),则y的值为11111111 10011100b(变成了十进制的-102);

但是,将要扩展的短数据类型变成无符号数后--unsigned char x=10011100b(即0x9A)

在 int y=(int)x时--进行扩展的时候是以零扩展,即用零来填充长数据类型的高字节位,则y的值应为00000000 10011100b(十进制的154)。

正确代码:

util=(int)*p++;改成util=(int)(unsigned char)*p++

Bug定位:

该bug是在code review的过程中发现的。

char符号扩展的问题,如果在测试时没有构造相应的case,就会很难被发现。面对这类问题,细致的code review是必不可少的,不管是通过code review直接发现问题还是通过review来丰富相应case的构造,code review都应该是一个不可缺少的环节。

关于符号扩展

一、短数据类型扩展为长数据类型

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;

二、长数据类型缩减为短数据类型

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

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

直接将内存中的数据赋给要转化的类型,数值大小则会发生变化。另短类型扩展为长类型时,但短类型与长类型分属有符号数与无符号数时,则先按规则一进行类型的扩展,再按本规则直接将内存中的数值原封不动的赋给对方。

附:有符号数的转换




方法


char


short


符号位扩展


char


long


符号位扩展


char


unsigned char


最高位失去符号位意义,变为数据位


char


unsigned short


符号位扩展到short;然后从short转到 unsigned short


char


unsigned long


符号位扩展到long; 然后从long 转到unsigned long


char


float


符号位扩展到long; 然后从long 转到float


char


double


符号位扩展到long; 然后从long 转到double


char


long double


符号位扩展到long; 然后从long 转到long double


short


char


保留低位字节


short


long


符号位扩展


short


unsigned char


保留低位字节


short


unsigned short


最高位失去符号位意义,变为数据位


short


unsigned long


符号位扩展到long; 然后从long转到unsigned double


short


float


符号位扩展到long; 然后从long 转到float


short


double


符号位扩展到long; 然后从long 转到double


short


long double


符号位扩展到long; 然后从long 转到double


long


char


保留低位字节


long


short


保留低位字节


long


unsigned char


保留低位字节


long


unsigned short


保留低位字节


long


unsigned long


最高位失去符号位意义,变为数据位


long


Float


使用单精度浮点数表示。可能丢失精度。


long


double


使用双精度浮点数表示。可能丢失精度。


long


long double


使用双精度浮点数表示。可能丢失精度。

无符号数的转换




方法


unsigned char


char


最高位作为符号位


unsigned char


short


0扩展


unsigned char


long


0扩展


unsigned char


unsigned short


0扩展


unsigned char


unsigned long


0扩展


unsigned char


float


转换到long; 再从 long 转换到float


unsigned char


double


转换到long; 再从 long 转换到double


unsigned char


long double


转换到long; 再从 long 转换到double


unsigned short


char


保留低位字节


unsigned short


short


最高位作为符号位


unsigned short


long


0扩展


unsigned short


unsigned char


保留低位字节


unsigned short


unsigned long


0扩展


unsigned short


float


转换到long; 再从 long 转换到float


unsigned short


double


转换到long; 再从 long 转换到double


unsigned short


long double


转换到long; 再从 long 转换到double


unsigned long


char


保留低位字节


unsigned long


short


保留低位字节


unsigned long


long


最高位作为符号位


unsigned long


unsigned char


保留低位字节


unsigned long


unsigned short


保留低位字节


unsigned long


float


转换到long; 再从 long 转换到float


unsigned long


double


Convert directly to double


unsigned long


long double


转换到long; 再从 long 转换到double

---------------------------------------------------------

符号扩展,零扩展,以及缩减

数现代高级程序设计语言允许程序员使用包含不同大小的整数对象的表达式。那么,当一个表达式的两个操作数大小不同的时候,会发生什么呢?有些语言会报错,而其他的语言则会自动将操作数转换成一个统一的格式。这种转换是有代价的,因此,如果你不希望编译器在你不知情的情况下自动加入各种转换到你原本非常完美的代码中,就需要掌握编译器是如何处理这些表达式的。

进制补码系统中,同一个负数在不同大小的表示法中的表示是不同的。你不能在一个包含16位数的表达式中随意地使用8位有符号数,转换是必需的。这种转换,以及其逆操作(将16位数转换为8位)就是符号扩展(sign extension)与缩减(contraction)操作。

-64为例,其8位的二进制补码表示是$C0,而等效的16位二进制补码表示则是$FFC0。很显然,其位模式不一样。再看看数+64,其8位和16位表示分别是$40与$0040。一个很显然的事实就是,扩展负数的大小与扩展非负数的大小是完全不同的。

个数从某个位数符号扩展到一个更大的位数很简单,只需要将符号位复制到新格式新增的高端各位即可,例如,为了将一个8位的数符号扩展到16位,只需将8位数的第7位复制到16位数的第8 .. 15位即可。而将一个16位数符号扩展到一个双字,只需要将第15位复制到双字的第16 .. 31位即可。

理不同长度有符号数的时候,必须使用符号扩展。例如,在将一个字节量与一个字量相加的时候,在相加之前必须将字节量符号扩展到16位。其他运算可能又会需要符号扩展到32位。

表2-5  符号扩展举例


8位


16位


32位


二进制补码表示


$80


$FF80


$FFFF_FF80


11_1111_1111_1111_1111_1111_1000_0000


$28


$0028


$0000_0028


00_0000_0000_0000_0000_0000_0010_1000


$9A


$FF9A


$FFFF_FF9A


11_1111_1111_1111_1111_1111_1001_1010


$7F


$007F


$0000_007F


00_0000_0000_0000_0000_0000_0111_1111


n/a


$1020


$0000_1020


00_0000_0000_0000_0001_0000_0010_0000


n/a


$8086


$FFFF_8086


11_1111_1111_1111_1000_0000_1000_0110

处理无符号二进制数的时候,可以使用零扩展(zero extension)来将小位数的无符号数扩展到大位数的无符号数。零扩展非常简单——只需要用零来填充大位数操作数的高端各个字节即可。例如,为了将8位数$82零扩展到16位,只需要在高端字节中插入零,即得到$0082。

表2-6  零扩展举例


8位


16位


32位


二进制补码表示


$80


$0080


$0000_0080


00_0000_0000_0000_0000_0000_1000_0000


$28


$0028


$0000_0028


00_0000_0000_0000_0000_0000_0010_1000


$9A


$009A


$0000_009A


00_0000_0000_0000_0000_0000_1001_1010


$7F


$007F


$0000_007F


00_0000_0000_0000_0000_0000_0111_1111


n/a


$1020


$0000_1020


00_0000_0000_0000_0001_0000_0010_0000


n/a


$8086


$0000_8086


00_0000_0000_0000_1000_0000_1000_0110

大多数高级语言编译器会自动处理符号扩展与零扩展,以下C语言的例子说明了它们是如何工作的:

signed char sbyte;     // C语言中的字符类型是一个字节

short int sword;           // C语言中的短整型一般是16位

long int sdword;           // C语言中的长整型一般是32位

. . .

sword = sbyte;         //自动将8位值符号扩展到16位

sdword = sbyte;            //自动将8位值符号扩展到32位

sdword = sword;            //自动将16位值符号扩展到32位

语言(例如Ada)在从小数据类型转换到大数据类型时需要显式转换(explicit cast)。查一下所用语言的参考手册就知道这种显式转换是不是必需的了。要求提供显式转换的语言的优点在于编译器永远不会在程序员不知情的情况下做任何事情。如果你没有提供必要的转换,编译器会给出一个诊断消息,让你知道程序还需要改进。

符号扩展和零扩展,有一点需要明确的是,它们是需要付出代价的。将一个小整型赋值给一个大整型可能会比在同样大小的整型变量间传输数据需要更多的机器指令(执行时间更长)。因此,在一个数学表达式或者一条赋值语句中混合使用不同大小的变量要小心。

符号缩减,即将一个某位数转换为值相同但位数变小的数,比较麻烦。符号扩展永远不会失败,使用符号扩展,一个m位有符号数永远可以转换为一个n位数(这里n>m)。不幸的是,在m的情况下,一个n位数不是总能转换为m位数。例如,-448的16位十六进制表示是$FE40,而这个数的大小对于8位来说太大了,我们无法将其符号缩减到8位。

将一个数值正确地符号缩减,必须要检查需要丢弃的高端字节。首先,这些高端字节必须是全零或者$FF,如果它们包含其他值,我们就无法对这个数进行符号缩减。其次,最终结果的最高位必须与被丢弃的所有位一致。以下就是一些从16位数转换到8位数的例子:

$FF80 (11_1111_1000_0000) 可以被符号缩减为 $80 (00_0000).

$0040 (00_0000_0100_0000) 可以被符号缩减为 $40 (00_0000).

$FE40 (11_1110_0100_0000) 不能被符号缩减为8 位

$0100 (00_0001_0000_0000) 不能被符号缩减为8 位

级语言里使用缩减有点困难,有些语言,譬如说C语言,会直接将表达式的低端部分存储到比较小的变量中,并将高端部分丢弃(在最好的情况下,C编译器可能会在编译过程中给出一个警告,提示可能会出现的精度损失)。你可以采取措施来让编译器停止抱怨,但是它仍然不会检查数值的有效性。以下是C语言中符号缩减的典型代码:

signed char sbyte;     // C语言中的字符类型是一个字节

short int sword;           // C语言中的短整型一般是16位

long int sdword;           // C语言中的长整型一般是32位

. . .

sbyte = (signed char) sword;

sbyte = (signed char) sdword;

sword = (short int) sdword;

语言中,唯一安全的解决方案就是在将表达式的结果值存储到一个小变量中之前,将该结果值与某个上下边界值进行比较。不幸的是,如果需要经常做这种操作,代码会变得比较笨拙。以下就是加上这些检查之后的转换代码:

if( sword >= 128 && sword <= 127 )

{

sbyte = (signed char) sword;

}

else

{

// 报告错误

}

// 另一种方案,使用断言:

assert( sword >= 128 && sword <= 127 )

sbyte = (signed char) sword;

assert( sdword >= 32768 && sdword <= 32767 )

sword = (short int) sdword;

易见,这让代码变得丑陋。在C/C++中,你可能会倾向于将它们编写为宏(#define)或者函数,以提高代码的可读性。

有些高级语言(例如Pascal和Delphi/Kylix)会自动进行符号缩减,还会检查结果来确保它适用于目标操作4。这些语言在越界违例发生的时候会产生某种类型的异常(或者停止程序的运行)。当然了,如果你想加入纠错代码,要么就需要写点异常处理代码,要么就使用前面C语言例子中使用的if语句序列。

引用:

http://testing.etao.com/experience_list/66

http://apps.hi.baidu.com/share/detail/40431986

http://blog.sina.com.cn/s/blog_6adcb3530101cmsd.html

时间: 2024-10-08 21:26:51

char 变成int型后的符号位扩展的相关文章

关于符号位扩展你又知道多少

转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/37535577 作者:小马 先看两段代码, 一个是C,一个是java. int _tmain(int argc, _TCHAR* argv[]) { char b = 0x83; short s1 = (short)b; short s2 = (short)(b&0xff); printf("s1 = %d\n", s1); printf("s2 = %

浅谈Java中的补零扩展和补符号位扩展

今天,魏屌出了一道题,题目如下: 定义一个大头序的byte[]a={-1,-2,-3,-4},转换成short[]b.问b[0]和b[1]分别是多少? 乍一看,这题不难,无非就是移位操作,再进行组合.但是呢?对于用Java的童鞋来说,这里面有一个坑,稍不注意可能就踩进去了.在说之前,我先把代码和答案贴出来吧. 看到这里,可能有的童鞋比较奇怪,为啥要&0xff,这不相当于没变化吗?非也,不信我举个例子. 答案是-127和129.很奇怪不是吗?我想的明明都是-127啊!!! 解答这个问题之前,我们先

有符号数,符号位扩展

char readbuf[64]; int data_size = (readbuf[7]<<24)|(readbuf[4]<<16)|(readbuf[5]<<8)| readbuf[6]; int data_size = (readbuf[7]<<24)+(readbuf[4]<<16)+(readbuf[5]<<8)+readbuf[6]; readbuf :  09 00 3c 16 00 00 96 00 00 00 00

C++ 中注意,零扩展和符号位扩展

版权声明:本文为博主原创文章,未经博主允许不得转载. 首先,介绍一下两种扩展的定义 转 http://blog.csdn.net/jaylong35/article/details/6160736 符号扩展:当用更多的内存存储某一个有符号数时,由于符号位位于该数的第一位,扩展之后,符号位仍然需要位于第一位,所以,当扩展一个负数的时候需要将扩展的高位全赋为1.对于正数而言,符号扩展和零扩展是一样的,因为符号位就是0. 比如一个用一个8位二进制表示-1,则是10000001 如果把这个书用16位二进

ipv4地址向int型转换,int型数据向ipv4转换。

将一个ip地址格式的字符串转换为一个int型(4字节32位)的数据.例如 char *p = "192.168.001.001"; 函数实现如下: int ipv4_to_int(char *ip) { int tmp = 0; char ip1, ip2, ip3, ip4; ip1 = atoi(ip); ip = strchr(ip, '.'); if(!ip) return -1; ip2 = atoi(++ip); ip = strchr(ip, '.'); if(!ip)

c语言中获取整数和浮点数的符号位

1. 为什么要获得符号位 很多时候,我们需要判断数值的正负,来做相应的逻辑处理.条件判断语句可以很好的完成这个需求.有时候会有下面的情况, if (x > 0) { x = x - 1; } else { x = 1 - x; } if (x < 0) { x = -x; } 正负只是数值的符号位变化,或是计算结果的符号位变化.但是我们需要用一个判断,首先条件判断会影响效率,其次格式不够简洁美观.所以,有时候希望能不用条件判断也解决问题.而数值的符号位已经被存储在了数值的最高位,可以利用这点来

C语言:int型指针与char型指针本质

---恢复内容开始--- 我的理解是,不管定义时将指针定义成何种类型,指针本质上都是一个数字,其位数由编译器来决定. 比如我的编译器是32位,那么无论是定义一个char *pc还是定义一个int *pi,本质上pc与pi都是一个32位的数,但是由于"char *" 和 "int *"的限制,所以pc++是自加8bit,而pi++是自加16bit. 1 // int指针与char指针.cpp : 2 // 看看“char型指针”与“int型指针”本质上有什么区别 3

【c语言】写一个宏定义,实现的功能是将一个int型的数的奇偶位互换

// 写一个宏定义,实现的功能是将一个int型的数的奇偶位互换,例如6的二进制为0110,第一位与第二位互换, // 第三位与第四位互换,得到1001,输出应该为9 #include <stdio.h> // 取出奇数位与偶数位,奇数位左移1位,偶数位一位,相加或者相或 #define EXCHANGE(num) (((0x55555555 & num) << 1) + ((0xaaaaaaaa & num) >> 1)) int main() { pr

int型、char*、string、的swap算法

1.俩整数,不使用中间变量交换其值: int& intswap(int& a, int& b) { b ^= a; a ^= b; b ^= a; return b; } 2.C++中俩string交换字符串 string & strswap(string & a, string & b) { a=a.append(b); b= a.substr(0,a.length()-b.length()); a=a.substr(b.length(),a.length