unsigned char和signed char型变量的存储和表示

#include <stdio.h>    
int main(void)  
{  
    signed char a = -1; 
    unsigned char b = -1;  

    printf("%%d:\n");
    printf("%d\n", a);  
    printf("%d\n", b);  

    printf("\n%%u:\n");  
    printf("%u\n", a);  
    printf("%u\n", b);  
    return 0;  
}   

%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输出值即可。

 下面代码是另一种问题:

#include <stdio.h>
int main(void)  
{  
	signed char a = 128;  
	signed char b = -128;  
	
	printf("%%d:\n");  
	printf("%d\n", a);  
	printf("%d\n", b);  
	
	printf("\n%%u:\n");  
	printf("%u\n", a);  
	printf("%u\n", b);  
	
	return 0;  
}     

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

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

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

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

%u格式,易得值为. signed char -128.最高位为后八位为1000 0000存储时被截断为1000 0000. 同理可分析,%d和%u格式与128输出相同.

 该使用char还是unsigned char

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

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

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

假设有:  

    char buf[N];  

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

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

如果直接这样:

     process(buf+userN, size)  

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

    unsigned char ucBuf[N] = {0}; 

    memcpy(ucBuf, buf+userN, size);  

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

    proccess(ucBuf, size)  

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

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

 void proccess(char *buf, size_t sz) 
 {   unsigned char ucBuf[N] = {0}; memcpy( ucBuf, buf, sz );   ...   }  

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

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 

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

include <stdio.h>   
void f(unsigned char v)     
{  
	char c = v;  
	unsigned char uc = v;  
	unsigned int a = c, b = uc;  
	int i = c, j = uc;  
	
	printf("----------------\n");  
	printf("%%c: %c, %c\n", c, uc);  
	printf("%%X: %X, %X\n", c, uc);  
	printf("%%u: %u, %u\n", a, b);  
	printf("%%d: %d, %d\n", i, j);  
}  

int main(int argc, char *argv[])  
{  
	f(0x80);  
	f(0x7F);   
	
	return 0;  

} 

输出结果:
----------------  

%c: ?, ?  
%X: FFFFFF80, 80 
%u: 4294967168, 128 
%d: -128, 128 

---------------- 
%c: ,   
%X: 7F, 7F  
%u: 127, 127  
%d: 127, 127  

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

 

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

#include <iostream>
#include <string>  
using namespace std;  

void convertUnCharToStr(char* str, unsigned char* UnChar, int ucLen)  
{  
	int i = 0;  
	for(i = 0; i < ucLen; i++)  
	{  	
		//格式化输str,每unsigned char 转换字符占两位置%x写输%X写输  
		sprintf(str + i * 2, "%02x", UnChar[i]);  
	}  
}  

void convertStrToUnChar(char* str, unsigned char* UnChar)  
{  
	int i = strlen(str), j = 0, counter = 0;  
	char c[2];  
	unsigned int bytes[2];  
	
	for (j = 0; j < i; j += 2)   
	{  
		if(0 == j % 2)  
		{  
			c[0] = str[j];  
			c[1] = str[j + 1];  
			sscanf(c, "%02x" , &bytes[0]);  
			UnChar[counter] = bytes[0];  
			counter++;  
		}  
	}  
	
	return;  
}  

int main()  
{  
	unsigned char src[6] = {0x12, 0x32,0x56,0x78,0x90,0xab};  
	char buffer[20];//维数定义些  
	convertUnCharToStr(buffer, src, 6);    
	printf("%s\n", buffer);  
	
	unsigned char dst[6];  
	int len = strlen(buffer);  
	cout << len << endl;  
	convertStrToUnChar(buffer, dst);  
	
	int i = 0;  
	for(i = 0; i < 6; i++)  
	{  
		printf("%x ", dst[i]);  
	}  
	cout << endl; 
	
	return 0; 
}  

猜你喜欢

转载自blog.csdn.net/heybeaman/article/details/80970386
今日推荐