int main() {
//0x80是有歧义的,可以表示128,也可以表示-128
// 正数 0x01 0x02... 0x7E 0x7F 0x80 0x81
// 1 2 126 127 128 129
// 零 0x00
// 负数(补码表示) 0xFF 0xFE... 0x82 0x81 0x80
// -1 -2 -126 -127 -128
#undef CHAR
#define CHAR unsigned char
CHAR c = 0x80;
int x = c; //x == 128
#undef CHAR
#define CHAR signed char
CHAR d = 0x80;
int y = d; //y == -128
//由上述代码可知,CHAR的符号性(有符号或无符号),决定了对0x80的解释。
//由于c变量的类型是无符号的char,0x80不可能是负数,只可能是正数128
//由于d变量的类型是有符号的char,则0x80有两种解释方式,128或-128.
//C语言认为,负数的收尾位是1代表负数,而0x80的二进制为:0b1000000, 因此选择了-128
//所以d的值是-128
//而且,规定了signed char的范围是-128 ~ 127, 超过范围的正数128要做截断处理。截断的结果是-128
//编译器做出了警告:从0x80到d会截断
//这个知识有什么用呢?
char e = 0x80;
int z = e; //z的结果是什么?
//z的结果是依赖于编译器的。主要由以下规则确定
//1) char 等同于 unsigned char 或者 char等同于signed char 由实现(编译器厂家)决定。
// 因此这段代码将不会有“可移植性”了。
//2) char的长度,可能是1个字节,2个字节,也是有实现(编译器厂家)决定的。
// 在char是2个字节时,不管它是有符号的,还是无符号的,结果都是128
//因此建议char类型做算数运算,或位运算时,不要依赖于下面的“常规假设”:char是8位的,char是有符号的。
//例如: 如果“常规假设”是成立的,也适用于大部分编译器,则
{
char a = 0x01;
char b = 0x80;
int x = a + b; //a (promote to int) ==> 0x00000001 ; b( promote to int ) 0xFFFFFF80
//补码的好处就是,正数和负数运算时,可以直接做加法运算。因此 x == 0xFFFFFF81
int y = a ^ b; //a (promote to int) ==> 0x00000001 ; b( promote to int ) 0xFFFFFF80
//做位逻辑运算时,也是按位的运算,只可惜b提升为int后,会出现一大堆FFFFFF,因此y == 0xFFFFFF81
}
//例如: 如果“常规假设”是不成立的:char是无符号的,char是8位的
{
char a = 0x01;
char b = 0x80;
int x = a + b; //a (promote to int) ==> 0x00000001 ; b( promote to int ) 0x00000080
//x == 0x00000081
int y = a ^ b; //a (promote to int) ==> 0x00000001 ; b( promote to int ) 0x00000080
//做位逻辑运算时,也是按位的运算,因此y == 0x00000081
}
//例如: 如果“常规假设”是不成立的:char是有符号的,char是16位的
{
char a = 0x01;
char b = 0x80;
int x = a + b; //a (promote to int) ==> 0x00000001 ; b( promote to int ) 0x00000080
//x == 0x00000081
int y = a ^ b; //a (promote to int) ==> 0x00000001 ; b( promote to int ) 0x00000080
//做位逻辑运算时,也是按位的运算,因此y == 0x00000081
}
//当char仅仅是当做存储二进制数据的容器使用时,或者当做字符的存储容器时,就比较安全,不用考虑符号性。
return 0;
}
char, 到底是个什么怪物?
最新推荐文章于 2024-05-15 12:19:54 发布