一.基本原理

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;
}


输出结果如下:

暂时总结到这里

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐