char 和 unsigned char 的区别
一.基本原理
1、char取值范围是 -128~+127。
2、我们先来看 signed char的最大值。
最高位是 符号位, 0 代表正数; 1 代表负数。0 1 1 1 1 1 1 1 这个值等于 2^0 + 2^1 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 = 127 。 也有一个简便计算方法:2^7 -1 = 128-1 =127 所以,signed char 的最大值是 127 。
singed char 的最小值计算。 在计算机中,数值是以补码形式存储的。正数的补码是其本身; 而负数的补码是 取反 (符号位保持不变),再加1。
我们先来看 -1 的存储过程:
-1: 1 0 0 0 0 0 0 1 ->(取反) 1 1 1 1 1 1 1 0 ->(加1) 1 1 1 1 1 1 1 1
-2: 1 0 0 0 0 0 1 0 ->(取反) 1 1 1 1 1 1 0 1 ->(加1) 1 1 1 1 1 1 1 0
-3: 1 0 0 0 0 0 1 1 ->(取反) 1 1 1 1 1 1 0 0 ->(加1) 1 1 1 1 1 1 0 1
… …
-127 1 1 1 1 1 1 1 1 ->(取反) 1 0 0 0 0 0 0 0 ->(加1) 1 0 0 0 0 0 0 1
-128 1 0 0 0 0 0 0 0 ->(取反) 1 1 1 1 1 1 1 1 ->(加1) 1 0 0 0 0 0 0 0
不管是原码还是补码, 总会出现 -0 这个值。 本来 +0 与 -0 是没有区别的。 但对于存储器和编译器来讲,总得充分利用每个字节吧, 更不想出现二义性。 所以,将 -0 表示为 -128 , 这样既增大了signed char 数值的表示范围,又消除了 -0 的二义性。
所以signed char的取值范围是 -128~127。
3.unsigned char没有符号位 最大值是 255(1 1 1 1 1 1 1 1)
我们看下下面的代码测试程序,就会清晰很多了
#include<iostream>
using namespace std;
int main(int argc, char * argv[])
{
//正数在计算机中存储的是源码,而负数存储的是补码
//在赋值的过程中,就是把计算机中存储的内容直接赋给对应的变量
//只是有符号数直接会把他当成补码处理,再对其进行减一取反操作
//至少在我这里是这样的看的。然后看他的符号位。
//没符号的数 就会直接按照源码处理,因为没有符号位,直接翻译过来。
//对于负数计算机存储的是补码,而对于正数计算机存储的是源码
char a = 0x81;
/*
//这里就匹配不上了,因为0x81 在计算机存储的是源码 1 0 0 0 0 0 0 1
而赋值给一个有符号的char 会当作补码进行处理,先对其减一
1 0 0 0 0 0 0 0,再对除符号位外进行取反。
1 1 1 1 1 1 1 1,
然后得到解析数值 -127,显然-127和+129显然是不相等的。
这里同时要注意在a的地址里存储的还是 10000001,只是把这个解析成补码了
*/
if (a == 0x81)
{
printf("you are matched!!!\n");
}
printf("a is %d\n", a);
//对其赋值给 unsigned char的时候,是会直接把a中在内存中存储的值
//10000001,而赋值给 unsigned char之后,unsigned char之后会当
//源码进行解析,则又恢复了原来的129.
unsigned char w = a;
printf("w is %d\n", w);
/*
-1 在计算机中按照补码存储 存储的数值是 11111111
把这个值赋值给一个 unsigned char 之后就会按照源码处理
没有符号位翻译成了 255
*/
char testc = -1;
unsigned char test_uc = testc;
printf("test_uc =%d\n", test_uc);
return 0;
}
输出内容如下:
a is -127
w is 129
test_uc =255
二.printf 输出的影响
在C中,默认的基础数据类型均为signed,如定义变量为int,long等,都为有符号的。如果要定义无符号类型,必须显式地在变量类型前加unsigned。
char vs unsigned char
相同点:在内存中都是一个字节,8位(2^8=256),都能表示256个数字
不同点:char的最高位为符号位,因此char能表示的数据范围是-128~127,unsigned char没有符号位,因此能表示的数据范围是0~255
实际使用中,如普通的赋值,读写文件和网络字节流都没有区别,不管最高位是什么,最终的读取结果都一样,在屏幕上面的显示可能不一样。
但是要把一个char类型的变量赋值给int、long等数据类型或进行类似的强制类型转换时时,系统会进行类型扩展,这时区别就大了。对于char类型的变量,系统会认为最高位为符号位,然后对最高位进行扩展,即符号扩展。若最高位为1,则扩展到int时高位都以1填充。对于unsigned char类型的变量,系统会直接进行无符号扩展,即0扩展。扩展的高位都以0填充。所以在进行类似的操作时,如果char和unsigned char最高位都是0,则结果是一样的,若char最高位为1,则结果会大相径庭。
#include <stdio.h>
/*
%d,%c,%s,%x是程序汇编语言中的格式符,它们的含义:
1、%d表示按整型数据的实际长度输出数据。//不会进行扩展到四个字节
2、%c用来输出一个字符。就输出一个字节里的内容,然后得到数值,不关心符号位,直接把对应的二进制变成一个正数
//找对应的acs码符号
3、%x表示以十六进制数形式输出整数。
//会扩展到四个字节 char类型的负数赋值给他影响较大,
所有高位都会扩展为1,其他位都不变(包括原来的符号位)
这个一般的办法是把高位清零来看,就是与0xff想与
4、%u表示以无符号十进制整数形式输出整数。
//会扩展到四个四节
char类型的负数赋值给他影响较大,
所有高位都会扩展为1,其他位都不变(包括原来的符号位)
*/
static void func(unsigned char uc)
{
char c;
int i, j;
unsigned int ui, uj;
c = uc;
i = (int)c;
j = (int)uc;
ui = (unsigned int)c;
uj = (unsigned int)uc;
printf("%%d: %d, %d\n", c, uc);//-128, //128
printf("%%c: %c, %c\n", c, uc);//直接输出二进制对应的数值
printf("%%x: %x, %x\n", c, uc);//-80 //)0X80
printf("%%u: %u, %u\n", ui, uj);//128 //128
printf("%%d: %d, %d\n", i, j);//-128 //128
printf("-------------------\n");
}
int main(int argc, char *argv[])
{
func(0x80);
func(0x7f);
int a = -2;
printf("a is :\n %%x:%x\n %%d:%d \n %%u:%u \n", a,a,a);
return 0;
}
输出结果如下:
暂时总结到这里
更多推荐
所有评论(0)