首先考虑下面代码输出什么?
[cpp] view plain copy
1. #include
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补码存储在它里面为 11111111(1字节。) signed int 为 11111111 11111111 11111111 11111111(假设此时int为4字节)。变量类型有无符号表示如何看待最高位。例如 -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
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输出相同.
这个问题似乎很简单,要表示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. }
在C中,默认的基础数据类型均为signed,现在我们以char为例,说明(signed) char与unsigned char之间的区别
首先在内存中,char与unsigned char没有什么不同,都是一个字节,唯一的区别是,char的最高位为符号位,因此char能表示-128~127, unsigned char没有符号位,因此能表示0~255,这个好理解,8个bit,最多256种情况,因此无论如何都能表示256个数字。
在实际使用过程种有什么区别呢?
主要是符号位,但是在普通的赋值,读写文件和网络字节流都没什么区别,反正就是一个字节,不管最高位是什么,最终的读取结果都一样,只是你怎么理解最高位而已,在屏幕上面的显示可能不一样。
但是我们却发现在表示byte时,都用unsigned char,这是为什么呢?
首先我们通常意义上理解,byte没有什么符号位之说,更重要的是如果将byte的值赋给int,long等数据类型时,系统会做一些额外的工作。
如果是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
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时,则有区别了。
1. #include
2. #include
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. }