前言
C语言中整形数据的存储主要有三个重点。1、原反补码 2、大小端 3、整形截断和提升
一、原反补码
原码、反码、补码是计算机中对数字的二进制表示方法。
原码:将最高位作为符号位(0表示正,1表示负),其它数字位代表数值本身的绝对值的数字表示方式。
反码:如果是正数,则表示方法和原码一样;如果是负数,符号位不变,其余各位取反,则得到这个数字的反码表示形式。
补码:如果是正数,则表示方法和原码一样;如果是负数,则将数字的反码加上1(相当于将原码数值位取反然后在最低位加1 。
用八位二进制数举个例子:
原码 | 反码 | 补码 | |
---|---|---|---|
1 | 00000001 | 00000001 | 00000001 |
-1 | 10000001 | 11111110 | 11111111 |
那么为什么会有原反补码存在呢? 原因是计算机只有加法器,没有减法器。
在计算器中,所有的减法计算都必须用加法进行,通过被减数的补码代替被减数,可以把减法转变为加法运算。
用上例中的两数相加试一试。
如果用原码计算:
可以看到答案是-2,但这并不是正确的结果,因此聪明的计算机先驱们创造了补码,利用补码相加:
这样一来,计算的答案就正确了!
二、大小端
计算机对数据的存储方式可分为大端存储和小端存储模式。
采用大小模式对数据进行存放的主要区别在于在存放的字节顺序,大端方式将高位存放在低地址,小端方式将低位存放在低地址。采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。到目前为止,采用大端或者小端进行数据存放,其孰优孰劣也没有定论。
在一个处理器系统中,有可能存在大端和小端模式同时存在的现象。这一现象为系统的软硬件设计带来了不小的麻烦,因此,判断系统到底是大端还是小段存储是非常终于重要的。
设计一个函数判断大小端:
代码一:
void bigorsmall()
{
int a = 1;
char b = *(char*)&a;
if(b==1)
printf("小端\n")
else
printf("大端\n");
}
代码二(利用联合体判断):
int bigorsmall()
{
union
{ int i;
char c;
}un;
un.i = 1;
return un.c;
}
如果返回1,则说明是小端机,0则是大端机。
三、整形的截断与提升
整形的截断和提升是给整形赋值时可能会发生的事件。
1.整形截断
机制 | 将字节数多的数据类型赋给字节数少的数据类型时触发 |
---|---|
效果 | 截取低位的数据,舍弃高位数据 |
2.整形提升
机制 | 将字节数少的数据类型赋给字节数多的数据类型时触发 |
---|---|
效果 | 1.无符号数->向多出的高位补0 ----------------- 2.有符号数->补符号位 |
举例说明
整形截断
int main()
{
unsigned char a = 200;
unsigned char b = 100;
unsigned char c = 0;
c = a + b;
printf(“%d ”, c);
return 0;
}
上面代码的输出结果是44。原因是由于200+100=300,但无符号char型的最大值是255,300的二进制数是100101100此时传值就会发生截断,舍弃最高位的1,剩下的低八位就是44了。
整形提升
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
上面代码的输出结果如下
这里为什么会输出一个这么大的数字呢,我们把它转换成二进制看看,转换成二进制的结果是11111111 11111111 11111111 10000000,可以看到前面那么多1的原因应该是进行了补位。
这就是整形提升的效果,来分析一下这段代码。
这里给a赋值的第一步首先进行了整形截断,-128的二进制是110000000,补码是110000000,截断后变成10000000(8位二进制数)。
然后需要%u打印时就要进行整形提升到32位,补充符号位后就变成了11111111 11111111 11111111 10000000,因此就能理解输出结果为什么这样了。