【C语言】基础知识(字节、进制之间的相互转换、源码、反码、补码、移位操作符、整形提升)

一:计算机的基础单位和数据类型

2.1 字节

bit------------ 比特
byte --------- 字节 ------------- 1byte = 8bit
KB ------------ 千字节 ---------- 1KB = 1024byte
MB ----------- 兆 ---------------- 1MB = 1024KB
GB ----------- 吉字节 ---------- 1GB = 1024MB
TB ----------- 太字节 ---------- 1TB = 1024GB
PB ----------- 拍字节 ----------- 1PB = 1024TB

2.2 基本类型的字节长度

char -------- 1字节
short --------- 2字节
int -------- 4字节
long ---------- 4字节
long long -------- 8字节
float ---------- 4字节
double ---------- 8字节

二:进制

2.1 计算机的语言表示

在现代的计算机中主要采用的数字集成电路完成,数字电路通过高低电平只能表示0和1,所以就出现了,计算机只会识别0和1。无论是存储还是计算,计算机均采用二进制体系完成。
例如:十进制 4 ---------- 用二进制表示为 100

2.2 各进制的概念

常用的进制包括:二进制、八进制、十进制与十六进制,它们之间区别在于数运算时是逢几进一位。比如二进制是逢2进一位,十进制也就是我们常用的0-9是逢10进一位。

二进制:二进制,在计算机内部,数据都是以二进制的形式存储的。用0、1两个数字来表示数值,这就是二进制(Binary)。例如,数字 0、1、10、111、100、1000001 都是有效的二进制。

八进制:八进制,Octal,缩写OCT或O,采用0、1、2、3、4、5、6、7八个数字,逢八进一。

十进制:十进制,是日常生活中使用、接触最多的,所有的数字都用10个基本的符号标识,满十进一。

十六进制:十六进制,是计算机中数据的一种表示方式,它是由0-9和A-F组成,字母不区分大小写。

2.3 二进制、八进制、十进制、十六进制的相互转换

2.3.1 十进制转二进制

计算方法

  1. 十进制的数除以2,得到商和余数(余数为0或1)
  2. 然后得出的商继续除以2得到商和余数,直至商为0
  3. 最后将这些余数倒序排序

十进制数和二进制数的前几个数对应表如下:
在这里插入图片描述

十进制数除2取余法,即十进制数除2,余数为权位上的数,得到的商值继续除2,依此步骤继续向下运算直
到商为0为止。
在这里插入图片描述
注意:1除以任何一个大于1的正整数的余数都是1

// 十进制数256 转二进制为100000000
256/2=128 余0
128/2=64 余0
64/2=32 余0
32/2=16 余0
16/2=8 余0
8/2=4 余0
4/2=2 余0
2/2=1 余0
1/2=0 余1 // 1除以任何一个大于1的正整数的余数都是1
将余数倒序排序就是二进制为:100000000

2.3.2 二进制转十进制

计算方法

  1. 从最后一位开始算,依次乘以2的n次方
  2. 最后一位就是2的0次方,倒数第二位就是2的一次方,2的次方一直+1,直至第一位
  3. 最后将所有计算的和加起来

注意:任何非零数的0次方都等于1

// 二进制100000000 转为十进制256
// 从最后一位开始算,为2的0次方,之后每次乘以2的上一次次方+1
0*2的0次方=0 // 任何非零数的0次方都等于1
0*2的1次方=0 
0*2的2次方=0 
0*2的3次方=0 
0*2的4次方=0 
0*2的5次方=0 
0*2的6次方=0 
0*2的7次方=0
1*2的8次方=256 // 等于1*2*2*2*2*2*2*2*2=256
// 最后相加 256+0+0+0+0+0+0+0+0=256

// 二进制1101011转为十进制107
// 从最后一位开始算,为2的0次方,之后每次乘以2的上一次次方+1
1*2的0次方=1 // 任何非零数的0次方都等于1
1*2的1次方=2 
0*2的2次方=0 
1*2的3次方=8 
0*2的4次方=0 
1*2的5次方=32
1*2的6次方=64  // 等于1*2*2*2*2*2*2=64
// 最后相加 64+32+0+8+0+2+1=107

2.3.3 八进制转二进制

八进制数除2取余法,得到二进制数,对每个八进制数分为3个二进制单位,不足时左边补零
在这里插入图片描述
例:十进制226转化为二进制就是10010110

2.3.4 二进制转八进制

把二进制数按照3位为单位进行按权展开相加得到1位八进制数。
在这里插入图片描述
例:二进制10010110转化为八进制就是226

2.3.5 十六进制转二进制

十六进制数除2取余法,得到二进制数,对每个十六进制数分为4个二进制单位,不足时左边补零
在这里插入图片描述
例:十六进制12C转化为二进制就是100101100

2.3.6 二进制转十六进制

把二进制数按照4位为单位进行按权展开相加得到1位十六进制数
在这里插入图片描述

2.3.7 二进制转十六进制

方法一:间接法,把十进制转换成二进制,然后由二进制转换为八或十六进制

方法二:直接法,十进制数除8或16取余法,即十进制数除8或16,余数为权位上的数,得到的商值继续除8或16,直到为商0为止。
在这里插入图片描述

2.3.8 八或十六进制转十进制

把八或十六进制按权展开,相加即可得到十进制数
在这里插入图片描述

2.3.9 八进制转十六进制

将八进制转换为二进制,然后将二进制转换为十六进制
在这里插入图片描述

2.3.10 十六进制转八进制

将十六进制转换为二进制,然后将二进制转换为八进制
在这里插入图片描述

三:源码、反码、补码

3.1 概念

  1. 应用范围:源码、补码、反码只能应用在整数中:正整数、负整数
  2. 在正整数中:源码 =反码 =补码
  3. 在负整数中:如果是负数,将源码的符号位不变,其余各位取反,得到反码如果是负数,将反码加1,得到补码
  4. 重点区分:整形表达式计算使用在内存中的是补码。打印和看到的都是源码。

例:int a = 3

int a = 3 ; // int整型为4字节,32个bit位
//源码:00000000 00000000 00000000 00000011
//反码:00000000 00000000 00000000 00000011
//补码:00000000 00000000 00000000 00000011
//因为是正整数所以 源码=反码=补码

例:int a = -3

int a = -3 ; // int 为整型4个字节32个bit位
//因为是负数,所以最高位是 1
//源码:10000000 00000000 00000000 00000011
//源码符号位不变,其余各个位按位取反,得到反码
//反码:11111111 11111111 11111111 11111100
//反码+1,得到补码
//补码:11111111 11111111 11111111 11111101

3.2 理解

估计大家应该有和我开始学习时一样的想法,计算机直接使用二进制就好了,为啥要区分源码、反码、补码。其实对于计算机而言,实现加法相对简单,相反减法就显得有些复杂,需要考虑借位逻辑,很难实现且效率太低,慢慢的减法器就被加法器给替代了,使得算法更加的简单和高效。
下面我将依次对源码、反码、补码进行分析,并说明为什么,二进制运算使用补码。

3.2.1 源码

使用源码运算正整数加法时没有问题的,因为正整数的源码=反码=补码
两个正整数相加,二进制加下来也是10

int a = 5 ;  // int为4字节32个bit位 
int b = 5 ;  // 00000000 00000000 00000000 00000101  --二进制
int sum=0 ;
sum = a + b ;  // 00000000 00000000 00000000 00000101
               // 00000000 00000000 00000000 00000101   相加
// 此时sum = 10 
               // 00000000 00000000 00000000 00001010  ---二进制  

此时使用负整数去运算相加。

int a = 3;   // 00000000 00000000 00000000 00000011  3的源码=反码=补码
int b = -2;  // 10000000 00000000 00000000 00000010  -2的源码
             
int sum = 3 + (-2) ;  //相加
             // 10000000 00000000 00000000 00000101   此时结果为-5 

3.2.2 反码

针对于反码,其实弥补了源码不能实现减法运算的问题,但是最高位会发生进位,需要低位加1,此时的运算效率就会大大的降低。
计算3+(-2)就会发现结果为1。

int a = 3 ; // 00000000 00000000 00000011  源码=反码=补码
 
int b = -2 ;// 10000000 00000000 00000010  -2的源码
            // 11111111 11111111 11111101  -2的反码

//此时将两个反码相加
// 最高位多出的(1) 100000000 00000000 00000001  --1

计算1+(-1)

int a = 1 ;  // 00000000 00000000 00000000 00000001   源码=反码=补码
int b = -1 ; // 10000000 00000000 00000000 00000001   -1的源码
             // 11111111 11111111 11111111 11111110   -1的反码 
// 此时让 a+b 两个反码相加
             // 11111111 11111111 11111111 11111111      结果也为0 

这种情况发现 11111111 和 00000000 此时都为0,计算机中很难判断。所以得出结论反码能实现加减法,但是有瑕疵,且效率低。

3.2.3 补码

补码应用在计算机二进制,计算、存储的编码格式,同时解决了源码的缺陷和反码的瑕疵。其中在高位溢出是,可以直接丢弃。
3 + (-2)

int a = 3 ;  // 00000000 00000000 00000000 00000011 源码=反码=补码
int b = -2 ; // 10000000 00000000 00000000 00000010   -2的源码
             // 11111111 11111111 11111111 11111101   -2的反码
             // 11111111 11111111 11111111 11111110   -2的补码
 
//此时两个数的补码相加
             // 100000000 00000000 00000000 00000001 最高位溢出一个1,直接丢弃
     //最终结果  00000000 00000000 00000000 00000001   ----1

1+(-1)

int a = 1 ;  // 00000000 00000000 00000000 00000001 源码=反码=补码 
int b = -1 ; // 10000000 00000000 00000000 00000001   -1的源码
             // 11111111 11111111 11111111 11111110   -1的反码
             // 11111111 11111111 11111111 11111111   -1的补码
 
//此时两个数的补码相加
     //最终结果:100000000 00000000 00000000 00000000  做高位溢出一个1 ,直接丢弃
     //最终结果:00000000 00000000 00000000 00000000  ----0

最终 很去确切的输出了0,并没有反码那么繁琐,此时补码就体现出它不仅可以实现加减法运算而且算法运算更加的简单,计算效率更高。

四:移位操作符

4.1 左移操作符(<<)

左移操作符的移位规则是:左边抛弃、右边补0
我们来举个例子:

#include <stdio.h>
int main()
{
	int a = 10;
	int b = a << 1;
	printf("%d", b);
	return 0;
}

大家可以按照上面的移动规则自己算一下,看看结果是什么。
好,那现在我们一起来分析一下:
在这里插入图片描述
那结果是不是20 呢?我们来看一下:
在这里插入图片描述
另外提醒一下,对a进行移位之后,a在不被赋值的情况下(a=a<<1),a自身的值不会发生变化。

我们可以打印一下看看:
在这里插入图片描述
好,刚才是对正数进行移位,我们再来移一个负数试试:

int main()
{
	int a = -2;
	int b = a << 1;
	printf("%d", b);
	return 0;
}

一起分析一下:
在这里插入图片描述
是-4吗?
在这里插入图片描述
做了两道题之后,我们好像可以发现一个规律:

对整数左移一位,相当与乘了一个2:

10左移一位结果是20;
-2左移一位结果是-4;

那左移操作符我们学会了,接下来我们来看右移操作符。

4.2 右移操作符(>>)

对于右移操作符,它的移位规则分为两种:

4.3.1 算术右移:右边丢弃,左边补原符号位

上例子:

int main()
{
	int a = -1;
	int b = a >> 1;
	printf("%d", b);
	return 0;
}

在这里给大家提一下,在我使用的vs2022这个编译器上采用的就是算术右移(大部分编译器都是算术右移)。

那我们按照算术右移来分析一下结果是什么:

在这里插入图片描述
看看vs2022的结果:

在这里插入图片描述
整数算术右移一位相当于除以2并向下取整。(大家可以自己多试几个)

这是算术右移,接着我们看逻辑右移。

4.3.2 逻辑右移:右边丢弃,左边补0

那还是这个代码:

int main()
{
	int a = -1;
	int b = a >> 1;
	printf("%d", b);
	return 0;
}

现在我们用逻辑右移来分析一下:

在这里插入图片描述
我们看看转换为10进制是几:

在这里插入图片描述
因为我们的编译器是算术右移,所以没法验证,大家知道就行了!!!

警告:对于移位运算符,不要移动负数位,这个是标准未定义的。

比如:
在这里插入图片描述
说明:左移右移一定是向左、向右两个方向移动吗?
左移其实是向高位移动,右移其实是向低位移动,只不过一般情况下左边就是高位,右边就是低位,所以起名为左移右移,符合我们的正常习惯。

四:整形提升

4.1 什么是整型提升

整型提升是C程序设计语言中的一项规定:在表达式计算时,各种整型首先要提升为int类型,如果int类型不足以表示则要提升为unsigned int类型;然后执行表达式的运算。

4.2 整型提升的规则

整型提升分为有符号和无符号两种。

  1. 有符号的:整型提升时是按照变量的补码被截断时的最高位是什么进行补位的,如果截断后最高位(即最左面)的一位数为 1 则在最高位前补 1 ,如果最高位是 0 则在前面补 0 ,补够32位即int类型即可。
  2. 无符号的: 直接在被截断的前面补 0 即可。

4.3 例题一

int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d\nb=%d\nc=%d\n", a, b, c);
	return 0;
}

上面代码运行结果如图:(编译器计算过程中发生了整型提升与截断)
在这里插入图片描述
分析如图:
在这里插入图片描述
我们可以分成几步来看:

  1. 数字-1的补码为:11111111111111111111111111111111 , 将数字-1交给a时,因为a,b,c的类型为char类型即只有一个字节,所以a,b,c中只能储存一个字节(即8个比特位),所以需要进行截断只保留最后的8个比特位,所以此时a中储存的比特位为:11111111 (上图中有转换过程)。此时得到的是补码(因为整型数字在计算机中都是以补码形式存储)
  2. 以%d打印需要进行整型提升。a,b都是有符号数最高位为1,则在前面(最左边)补1得到:11111111111111111111111111111111. c为无符号数在前面(最左边)补0得到:00000000000000000000000011111111.
  3. 再转换成原码打印 。a,b原码:10000000000000000000000000000001(补码减一取反得到原码). c原码为:00000000000000000000000011111111(整数无符号数原码=反码=补码)。最终得到a=-1,b=-1,c=255

4.4 例题二

int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	printf("%d\n", c);
	return 0;
}

不细心的老铁一看c=130,但运行后结果是-126.为什呢?
在这里插入图片描述
结合下图分析:
在这里插入图片描述
步骤分析

  1. 数字3的补码为:00000000000000000000000000000011 , 将数字3交给a时,因为a的类型为char类型即只有一个字节,所以a中只能储存一个字节即8个比特位,所以需要进行截断只保留最后的8个比特位,所以此时a中储存的比特位为:00000011。数字127的补码为:00000000000000000000000001111111 同理也因为为char类型发生截断,截断后b中储存的比特位为: 01111111

  2. 执行 a+b 时先对8比特位的a,b进行整型提升,因为都为char 类型所以为有符号位,提升时补最高位的数字0,补够32位。提升后两者的补码为:00000000000000000000000000000011,00000000000000000000000001111111。将a,b相加得到补码:00000000000000000000000010000010

  3. 又 c 为char类型,只能存放8个比特位,所以需要截断,截断后c 中储存的比特位为:10000010

  4. 再以%d形式打印,需要32位比特位,要对 c 进行整型提升了。因为c 的最高位是 1 在最高位前面补 1 即可,补够32位,提升后补码为:11111111111111111111111110000010

  5. 将补码转化为原码的形式打印出来,转化后的原码为 : 10000000 0 00000000000000001111110为负数,原码对应的整数就为 -126

4.5 整型提升的意义

整型提升的意义在于:表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随意石光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值