unsigned char和signed char型变量学习

来源:互联网 发布:淘宝神笔是干什么的 编辑:程序博客网 时间:2024/06/03 16:42

首先考虑下面代码输出什么?

[cpp] view plain copy

1.  #include <stdio.h>  

2.    

3.  int main(void)  

4.  {  

5.    

6.      signed char a = -1;  

7.      unsigned char b = -1;  

8.    

9.      printf("%%d:\n");  

10.     printf("%d\n", a);  

11.      printf("%d\n", b);  

12.   

13.      printf("\n%%u:\n");  

14.     printf("%u\n", a);  

15.      printf("%u\n", b);  

16.   

17.      return 0;  

18.           

19.  }     


%d
格式下  a = -1  b = 255%u格式下  a = 4294967295  b = 255.

下面我们进行分析为什么会出现这种结果:

 

首先数字在内存中以他的补码形式保存, -1的补码为全1,这个不会因为他被赋予变量大小或变量类型有无符号而改变。变量大小改变的是存储此数字的位数,例如signed char 变量 -1补码存储在它里面为 111111111字节。) signed int 11111111 11111111 11111111 11111111(假设此时int4字节)。变量类型有无符号表示如何看待最高位。例如 -1 赋予一个 signed char变量11111111最高位1被当为符号位。 -1被赋予一个unsigned char变量  11111111最高位1被当作数值位(不再当作符号位)

 

此时我们来分析 signed char -1%d 形式输出。首先它以补码形式存储在此类型变量, 11111111最高位被当作符号位。因为要求以%d(十进制有符号整数类型)输出。

因此我们将他的补码补到32位。因为他为signed类型,所以补码补符号位。 11111111前面补24个符号位1即补码变为FFFFFFFF又因为以十进制有符号整数类型格式输出(将这个FFFFFFFF看作一个有符号数补码,将这个补码以%d格式进行解释,并输出),所以把最高位看为符号位,即他为一个负数。将此负数补码转换为原码可得 10000000 00000000 00000000 00000001所以输出-1.

同理分析%u  形式输出。补完符号位后补码变为FFFFFFFF此时以无符号十进制整数形式输出(即把这个FFFFFFFF看做一个无符号数的补码),我们将他最高位看做数值位,无符号数即大于等于0数。所以原码即补码,所以源码为FFFFFFFF的二进制数值为4294967295.

 

接下来分析 unsigned char -1.  -1以补码形式存储在内存中的值为全1将他赋给一个unsigned char变量时, 11111111(依然是这种形式不变)。只是系统认为他的最高位不是符号位,为数值位。此时以%d格式输出,先进行补码补全。  因为此时为unsigned所以 11111111前面补数字0而不是符号位.  补全后补码变为 00000000 00000000 00000000 11111111 此时以%d格式打印。最高位为0,系统把他看做一个正数的补码,即原码也是这个。此原码值为255.

 

最后,以%u形式输出(系统认为此补码代表一个大于等于0数,所以即使最高位为1,也被当作数值位。而不是把他当作负数)。 00000000 00000000 00000000 11111111  %u格式把最高位当作数值位。值为255.

 

此代码可判断你的编译器char是什么类型,加上一条 char c = -1判断a.b.c输出值即可。

 

 

下面代码是另一种问题:

[cpp] view plain copy

1.  #include <stdio.h>  

2.    

3.  int main(void)  

4.  {  

5.    

6.      signed char a = 128;  

7.      signed char b = -128;  

8.    

9.      printf("%%d:\n");  

10.     printf("%d\n", a);  

11.      printf("%d\n", b);  

12.   

13.      printf("\n%%u:\n");  

14.     printf("%u\n", a);  

15.      printf("%u\n", b);  

16.   

17.      return 0;  

18.           

19.  }     

 

我们先进行分析,char变量为一个字节,八比特位。存储有符号数时最高位为符号位。128二进制形式为10000000.

有符号数128最高位 0后八位为10000000  存储在signed char变量中,因为此变量只有八个比特位空间,所以存储时被截断为 10000000.

此时,因为为signed char类型,最高位1被解释为符号位。

%d形式输出,先补全补码,补符号位1.     11111111 1111111111111111 10000000.  随后,%d格式,所以他被解释为一个负数的补码,转化为原码,数值为-128.

%u格式易得值为.

 

signed char -128.最高位为1后八位为100000000存储时被截断为10000000.同理可分析,%d%u格式与128输出相同.

 

 

该使用char还是unsigned char

这个问题似乎很简单,要表示8位无符号数值的时候,unsigned char,要表示8位有符号数值或者ASCII字符的时候,用char

但是,有的时候会遇到这样一种情况,从一串字符串中取出的字符,既有当字符使用的,也有当无符号8位数值使用的,这下就有点小纠结了,特别在定义接收字符串参数的函数时,参数该定义为unsigned char 类型的呢,还是定义成char类型的呢?

一种方法是按需要决定,如果函数里把它当无符号数值使用,就定义成unsigned char的参数,但是,这样还得把字符串当中用作无符号数值的部分,用另一个unsigned char数组存放起来,再传递给函数。否则会出现编译不通过的情况。

假设有:

[cpp] view plain copy

1.  char buf[N];  


要把其中当无符号数值使用的部分传给如下函数。

[cpp] view plain copy

1.  void process(unsigned char *puc, size_t sz)  

2.    

3.  {  

4.       ...  

5.  }  


如果直接这样:

[cpp] view plain copy

1.  process(buf+userN, size)  

GUN编译器下,会提示不能将参数char*类型转换在unsigned char*类型,因此我们还要开辟一个临时的unsigned char数组才行。如下:

[cpp] view plain copy

1.  unsigned char ucBuf[N] = {0};  

2.  memcpy(ucBuf, buf+userN, size);  


然后将unsigned char数组传递给函数:

[cpp] view plain copy

1.  proccess(ucBuf, size)  

这样的话,每一个使用这个函数的地方,都要做这个麻烦的步聚。

后来想了想,觉得将所有函数的接口都定义成接收char *类型的字符串,这样就不必考虑类型不匹配的问题了,然后在要直接计算的地方将char类型做相应的转换。

[cpp] view plain copy

1.  void proccess(char *buf, size_t sz)  

2.    

3.  {  

4.       unsigned char ucBuf[N] = {0};  

5.       memcpy( ucBuf, buf, sz );  

6.       ...  

7.  }  

unsignedcharsignedchar型变量的区别,赋值后它在内存中的存储形式

C中,默认的基础数据类型均为signed,现在我们以char为例,说明(signed) charunsigned char之间的区别 

首先在内存中,charunsigned char没有什么不同,都是一个字节,唯一的区别是,char的最高位为符号位,因此char能表示-128~127, unsigned char没有符号位,因此能表示0~255,这个好理解,8bit,最多256种情况,因此无论如何都能表示256个数字。

在实际使用过程种有什么区别呢?

主要是符号位,但是在普通的赋值,读写文件和网络字节流都没什么区别,反正就是一个字节,不管最高位是什么,最终的读取结果都一样,只是你怎么理解最高位而已,在屏幕上面的显示可能不一样。

但是我们却发现在表示byte时,都用unsigned char,这是为什么呢?

首先我们通常意义上理解,byte没有什么符号位之说,更重要的是如果将byte的值赋给intlong等数据类型时,系统会做一些额外的工作。

如果是char,那么系统认为最高位是符号位,而int可能是16或者32位,那么会对最高位进行扩展(注意,赋给unsigned int也会扩展)

而如果是unsigned char,那么不会扩展。

这就是二者的最大区别。

同理可以推导到其它的类型,比如short unsigned short等等。

 

注意:

若所赋的数值大于char,或int类型的数值范围,他们再内存中的存储是按若超过位数则会从低位截取8位输出。若是int类型,则从低位截取16位或32为输出。

例如

unsigned char uLen = 512;  //因为512这个数值已经超出char类型的最大数值范围255,所以会被截取低8位输出,而512的二进制是 0000 0010 0000 0000,所以,赋予uLen变量的值会是0

 

具体可以通过下面的小例子看看其区别

[cpp] view plain copy

1.  include <stdio.h>  

2.    

3.     

4.    

5.  void f(unsigned char v)  

6.    

7.  {  

8.    

9.      char c = v;  

10.   

11.      unsigned char uc = v;  

12.   

13.      unsigned int a = c, b = uc;  

14.   

15.      int i = c, j = uc;  

16.   

17.      printf("----------------\n");  

18.   

19.      printf("%%c: %c, %c\n", c, uc);  

20.   

21.      printf("%%X: %X, %X\n", c, uc);  

22.   

23.      printf("%%u: %u, %u\n", a, b);  

24.   

25.      printf("%%d: %d, %d\n", i, j);  

26.   

27.  }  

28.   

29.     

30.   

31.  int main(int argc, char *argv[])  

32.   

33.  {  

34.   

35.      f(0x80);  

36.   

37.      f(0x7F);   

38.   

39.      return 0;  

40.   

41.  }  

42.   

43.     

44.   

45.  输出结果:  

46.   

47.  ----------------  

48.   

49.  %c: ?, ?  

50.   

51.  %X: FFFFFF80, 80  

52.   

53.  %u: 4294967168, 128  

54.   

55.  %d: -128, 128  

56.   

57.  ----------------  

58.   

59.  %c: ,   

60.   

61.  %X: 7F, 7F  

62.   

63.  %u: 127, 127  

64.   

65.  %d: 127, 127  




由此可见,最高位若为0时,二者没有区别,若不为0时,则有区别了。

 

C语言char*字符串数组和unsignedchar[]数组的相互转换

1.  #include <iostream>  

2.  #include <string>  

3.  using namespace std;  

4.    

5.  void convertUnCharToStr(char* str, unsigned char* UnChar, int ucLen)  

6.  {  

7.      int i = 0;  

8.      for(i = 0; i < ucLen; i++)  

9.      {  

10.         //格式化输str,unsigned char 转换字符占两位置%x写输%X写输  

11.          sprintf(str + i * 2, "%02x", UnChar[i]);  

12.     }  

13.  }  

14.   

15.  void convertStrToUnChar(char* str, unsigned char* UnChar)  

16. {  

17.      int i = strlen(str), j = 0, counter = 0;  

18.     char c[2];  

19.      unsigned int bytes[2];  

20.   

21.      for (j = 0; j < i; j += 2)   

22.     {  

23.          if(0 == j % 2)  

24.         {  

25.              c[0] = str[j];  

26.             c[1] = str[j + 1];  

27.              sscanf(c, "%02x" , &bytes[0]);  

28.             UnChar[counter] = bytes[0];  

29.              counter++;  

30.         }  

31.      }  

32.     return;  

33.  }  

34.   

35.  int main()  

36. {  

37.      unsigned char src[6] = {0x12, 0x32,0x56,0x78,0x90,0xab};  

38.     char buffer[20];//维数定义些  

39.      convertUnCharToStr(buffer, src, 6);    

40.     printf("%s\n", buffer);  

41.    

42.   

43.      unsigned char dst[6];  

44.     int len = strlen(buffer);  

45.      cout << len << endl;  

46.     convertStrToUnChar(buffer, dst);  

47.        

48.     int i = 0;  

49.      for(i = 0; i < 6; i++)  

50.     {  

51.          printf("%x ", dst[i]);  

52.     }  

53.      cout << endl;  

54.   

55.    

56.     return 0;  

57.  }