在计算机内,有符号数有3种表示法:原码、反码和补码。
原码:原码是计算机中对数字的二进制定点表示方法,最高位为符号位,符号位为1为正,符号位为0为负。
优点:简单直观
缺点:不能直接进行运算
反码:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
补码:正数的补码与其原码相同;负数的补码是在其反码的末位加1
计算机中,所有的数值都是用补码来存储的。
+7原:
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
-7原:
1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
+7反:
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
-7反:
1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+7补:
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
-7补:
1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
-128特殊
-128
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
只有有符号的整数才有原码、反码和补码的!其他的类型一概没有。虽然我们也可以用二进制中最小的数去对应最小的负数,最大的也相对应,但是那样不科学,下面来说说科学的方法。还是说一个字节的整数,不过这次是有符号的啦,1个字节它不管怎么样还是只能表示256个数,因为有符号所以我们就把它表示成范围:-128-127。它在计算机中是怎么储存的呢?可以这样理解,用最高位表示符号位,如果是0表示正数,如果是1表示负数,剩下的7位用来储存数的绝对值的话,能表示2^7个数的绝对值,再考虑正负两种情况,2^7*2还是256个数。首先定义0在计算机中储存为00000000,对于正数我们依然可以像无符号数那样换算,从00000001到01111111依次表示1到127。那么这些数对应的二进制码就是这些数的原码。到这里很多人就会想,那负数是不是从10000001到11111111依次表示-1到-127,那你发现没有,如果这样的话那么一共就只有255个数了,因为10000000的情况没有考虑在内。实际上,10000000在计算机中表示最小的负整数,就是这里的-128,而且实际上并不是从10000001到11111111依次表示-1到-127,而是刚好相反的,从10000001到11111111依次表示-127到-1。负整数在计算机中是以补码形式储存的,补码是怎么样表示的呢,这里还要引入另一个概念——反码,所谓反码就是把负数的原码(负数的原码和和它的绝对值所对应的原码相同,简单的说就是绝对值相同的数原码相同)各个位按位取反,是1就换成0,是0就换成1,如-1的原码是00000001,和1的原码相同,那么-1的反码就是11111110,而补码就是在反码的基础上加1,即-1的补码是11111110+1=11111111,因此我们可以算出-1在计算机中是按11111111储存的。总结一下,计算机储存有符号的整数时,是用该整数的补码进行储存的,0的原码、补码都是0,正数的原码、补码可以特殊理解为相同,负数的补码是它的反码加1。
#include<stdio.h>
int main(void)
{
char c=0xff;
printf("c=%d\n", c);
unsigned int d=0xffffffff;
printf("d=%d\n", d);
printf("d=%u\n", d);
return 0;
}
输出结果为
c=-1
d=-1
d=4294967295
解析
char为一字节。一字节=8位。而系统默认的常量为4字节,32位。所以0xff赋值给c的时候要进行截取,截取低8位。
%d输出10进制有符号的数。
这里用16位来表示运算过程,32位一样
0xff
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
红色部分为截取的低八位11111111赋值给c
c
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
c的符号位为1
c为负
所以此时存储的为补码。要输出他的原值,就要反过来求原码
求原码方法。补码取反+1
c补
1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
所以打印结果为-1
unsigned int 声明的d为无符号整形
printf(“%d\n”,d);转化成有符号数输出,同上
%u表示输出无符号的整数,十进制。0xffffffff=4294967695
类型 | 所占字符 | 字节 |
char | 1 | 8 |
short | 2 | 16 |
int | 4 | 32 |
long long | 8 | 64 |
#include<stdio.h>
int main(void)
{
char a=-1;
printf("a=0x%x\n", a);
unsigned char c=-1;
printf("c=0x%x\n", c);
return 0;
}
输出结果为
a=0xffffffff
c=0xff
扩充
有符号数扩充时,填充符号位,符号位为0,填充0,符号位为1,填充1
无符号数扩充时,全部填充0.
溢出
#include<stdio.h>
int main(void)
{
char c=-0xff;
int d=-0xff;
printf("c=0x%x\n", c);
printf("d=0x%x\n", d);
return 0;
}
编译是报错
4.c: In function ‘main’:
4.c:5: warning: overflow in implicit constant conversion
输出结果为:
c=0x1
d=0xffffff01
char所表示的有符号数为-128-127
而-0xff为-255.超过范围。会造成溢出。
%d表示输出十进制整数,有符号。四字节
%u表示输出十进制整数,无符号。四字节
%x表示以十六进制输出数值在电脑内存中所表示的方法。四字节
有符号数和无符号数的运算
#include<stdio.h>
int main(void)
{
unsigned int a=10;
if( (a+(-60))>10 )
{
printf("(10+(-60))>10\n");
}
unsigned int b=1;
if( b<-1 )
{
printf("1<-1\n");
}
return 0;
}
输出结果为:
(10+(-60))>10
1<-1
解析:
当无符号数和有符号数进行运算时。有符号数自动转换为无符号数,隐式转换。
此例中-60进行隐式转换。转换为一个非常大的正数。
同理, -1也会转换为一个非常大的正数。
用%u输出结果会得到
10+(-60)=4294967246
-1=4294967295
所以下面的条件才会成立
问题:
#include<stdio.h>
int main(void)
{
unsigned char a=1;
if(a<-1)
{
printf("1<-1\n");
}
return 0;
}
输出结果为空
why?
a转换为int类型的为1
1<-1?显然不成立。
总而言之。计算机中数值的各种元素是建立在补码的基础上的,理解了补码,这些问题不难解答。