操作符(原码反码补码)

目录

前言:

原反补码:

位操作符:

 ^

>>

<<

总结: 

逻辑操作符

&&

|| 

其他操作符: 

sizeof

++ 

-- 

()

?:(三目操作符)

,(逗号运算符)

+=

%

整形提升

练习巩固: 


 

前言:

  我们学习编程语言就必须会里面的所有操作符,在了解操作符之前需要有一些基础知识,我们必须要了解这些知识才能更好的去看其他内容。

  那么接下来我就一一详细介绍,有特别需求者可以直接跳转目录。

原反补码:

  我们知道内存里面存放的是二进制数据,内存记录的是二进制,B是电脑存储的基本单位(字节),1字节有8个比特位,就是8个二进制序列。如下:

1B  =8bit=8比特

1KB=1024B=1024字节

1MB=1024KB=1,048,576字节

1GB=1024MB=1,073,741,824字节

1TB=1024GB=1,099,511,627,776字节

1PB=1024TB=1,125,899,906,842,624字节

1EB=1024PB=1,152,921,504,606,846,976字节

1ZB=1024EB=1,180,591,620,717,411,303,424字节 

1YB=1024ZB=1,208,925,819,614,629,174,706,176字节 

  如int类型,有4个字节,32个比特位。但是负数该如何表示呢?

       于是就把最高位代表符号位,1代表负数,0代表正数。我们一般定义的int a = 1,C语言已经默认是有符号的整形。

       进入正题,原码、反码和补码到底是什么呢?在内存中存储的数据其实是以补码的方式存在的,因为计算机其实只会加减和位运算,为了解决负数的计算不会出错,就发明了补码(具体原因可以暂时忽略)。

  1. 原码:将一个数字以二进制记录,最高位是符号位,负数的最高位是1,正数的最高位是0。
  2. 反码:符号位不变,其他位按位取反。
  3. 补码:将反码加1。

       我们来看看-1在内存中是如何占据的:操作符(原码反码补码)_第1张图片

       地址为方便表示将一个16进制位代表8个bit位(不影响阅读,详情请看进制的转换-CSDN博客)。 此时你就会考虑到,既然int有4个字节,32个比特位,那么如果把这32位全部填充成1是不是会有上限?明确的告诉你,确实如此。

操作符(原码反码补码)_第2张图片

       我们可以看到,当32位全部填充成1时,最大10进制无符号数整形(unsigned int)就保存的是4294967295。但是前面说最高位是符号位,我们一般定义的默认就是有符号的整形(signed int),那么如果最高位是0(就是正数),最高位就是少了一个1。

操作符(原码反码补码)_第3张图片

       所以看出整形保存的正数最大存储的数据是2147483647。关于原反补码的转换也有快捷方式,我们也可以将补码直接符号位不变,其他位按位取反以后加1直接得到原码,如下图:操作符(原码反码补码)_第4张图片

(声明:补码的出现是为了负数方便计算,所以正数的原、反、补码相同)

       有了以上基础,我们就可以无障碍阅读一下内容了。 

位操作符:

  这个操作符有两种意思。

  1. 按位与:位操作符,与数学中的与相似,两真则真,一假则假(真可以理解为1,假可以理解为0)。遇到负数时先转化为补码,之后按位与。如图(负数与正数按位与)操作符(原码反码补码)_第5张图片
  2. 取地址:因为每个变量在计算机中都有存储的空间,所以就有对应的地址编号(暂不用了解,涉及指针,详情请看指针(基础篇)-CSDN博客,也可以跳过,不影响阅读),此时就不再是位操作符。

  按位或:位操作符,一真则真,遇到负数时转化为补码,之后按位或。

操作符(原码反码补码)_第6张图片

 ^

  按位异或:位操作符,相同出零,相异出一。

操作符(原码反码补码)_第7张图片

 按位取反 :对一个数进行操作,是针对二进制位进行操作。

//这里可以忽略Printf的具体实现
void Printf(int a)
{
	int count = 32;
	while (count--)
	{
		printf("%d", (a >> count) & 1);
	}
}

int main()
{
	int a = 5;
	//对应的二进制位
	//00000000000000000000000000000101
	//~就是每一位取反
	//11111111111111111111111111111010

	//为方便讲述
	//此时我们使用函数打印其二进制序列
    //注:这不是printf函数
	Printf(~a);
	return 0;
}

操作符(原码反码补码)_第8张图片

>>

  右移操作符:将二进制位整体向右移,分为两种情况。

  • 算术右移:右边丢弃,左边补原符号位。通常是采用算数右移,右移时,先将数字转化为补码,之后右移,符号位不变,此时为转化后的补码,再将它转化为原码,得到二进制数,之后看符号位,将其转化为十进制即可。如图

    操作符(原码反码补码)_第9张图片

操作符(原码反码补码)_第10张图片

       每当我们右移一位时,和十进制规律一样,该数会2倍缩小。

操作符(原码反码补码)_第11张图片

  • 逻辑右移:右边丢弃,左边补零。因为一般不会使用逻辑右移,所以我们不再举例。

       到底是算术右移还是逻辑右移,是取决于编译器,大部分编译器上是算术右移。

<<

       对应的,该操作符是左移操作符,但是它没有像右移操作符一样分为逻辑左移和逻辑右移,只要进行左移,先将其转换为补码,之后最高位不变,左边补0即可。该数呈2倍增长。

操作符(原码反码补码)_第12张图片

操作符(原码反码补码)_第13张图片

       关于左右移万万不可移动负数位。操作符(原码反码补码)_第14张图片 

总结: 

       要想学好位操作符就一定要学好原反补码,这样才能更好的学习C语言。位操作符都是按照补码进行位操作的,同理,结果也是补码,所以要转换为原码得出正确结果。按位取反包括符号位。

       这里我们结合其他操作符来使用其他例子帮助小伙伴来更好的理解。操作符(原码反码补码)_第15张图片

逻辑操作符

&&

       逻辑与:逻辑操作符,一假则假,1&&0结果为0,5&&0结果为0,5&&3结果为1。

操作符(原码反码补码)_第16张图片

|| 

        逻辑或:逻辑操作符,一真则真,5||0结果为1。

操作符(原码反码补码)_第17张图片

其他操作符: 

sizeof

       ???这也算操作符?是的,它是函数也是操作符,计算该数据类型的大小。计算结果为无符号的整形。操作符(原码反码补码)_第18张图片

++ 

       ++可以理解为自增操作符,分为前置++和后置加加。

  • 前置++:先将该数自增1,之后赋值。
    int main()
    {
    	int a = 0, b = 0;
    	b = ++a;
    	//此时先执行++a,就是将a自增1
    	//之后赋值给b
    	printf("b = %d\n", b);
    	printf("a = %d\n", a);
    
    	return 0;
    }

    操作符(原码反码补码)_第19张图片

  • 后置++:先使用该数原本的值,之后该语句结束执行完成后,自增1。
    int main()
    {
    	int a = 0, b = 0;
    	b = a++;
    	//此时先将a的值赋给b
    	//之后将a自增1
    	printf("b = %d\n", b);
    	printf("a = %d\n", a);
    
    	return 0;
    }

    操作符(原码反码补码)_第20张图片

-- 

       也是分为 前置-- 和 后置--,其规则和 ++ 一样,这里我们不再过多赘述。

操作符(原码反码补码)_第21张图片

()

       注意,这也是一个操作符,是强制类型转换操作符。比如将浮点型类型强制转换为整形。

操作符(原码反码补码)_第22张图片

?:(三目操作符)

       什么东西?问号指数:满天星!这其实是三目操作符,它里面必须有变量。

       这其实是条件操作符:也称三目操作符,如a>b?a:b翻译的结果就是a>b吗?是大于b,就是a,否则就是b(记住是冒号)。

int main()
{
	//条件操作符/三目操作符
	int a = 10, b = 20;
	int max = 0;
	max = (a > b ? a : b);
	//翻译:a大于b吗?
	//大于b则max = a
	//小于b则max = b   
	printf("%d", max);
	return 0;
}

操作符(原码反码补码)_第23张图片

,(逗号运算符)

       从左到右依次进行,整个表达式的结果是最后一个表达式的结果。

int main()
{
	int a = 1, b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	printf("%d\n", c);
	return 0;
}

操作符(原码反码补码)_第24张图片

       这个操作符有两种意思。

  • 定义指针变量操作符:定义一个指针类型的变量。
  • 解引用操作符:若已经定义过指针变量,想通过该指针变量去访问指向的空间,就需要解引用。(详情请看指针(基础篇)-CSDN博客不影响阅读该文章)。

+=

       这个操作符其实就是……上图吧:

操作符(原码反码补码)_第25张图片

       当然其他的操作符也有这种用法。 

操作符(原码反码补码)_第26张图片

       这里还有很多类似的操作,我们不再一一赘述。 

%

       取模:其实就是除法取余数。

操作符(原码反码补码)_第27张图片

       负数也有取模规则,结果有第一个数的正负而定。操作符(原码反码补码)_第28张图片 

整形提升

       我们知道字符在内存中也是2进制序列,那么计算机到底是如何进行字符的操作呢?比如定义的是字符型,输出的是整形,就会有暗箱操作。

操作符(原码反码补码)_第29张图片

        表达式的整形运算要在CPU的响应预案算起件内执行,CPU内整型元算器的操作数的字节长度一般就是int的字节长度,同时也是CPU通过寄存器的长度。

       因此即使两个char类型相加,也是难以直接实现两个8bit位直接相加运算(虽然机器指令中可能有这种bit位相加的指令),所以表达式中各种长度小于int的整型值,都必须先转换为int或unsigned int,然后才送去CPU执行运算。

       在整形提升时,char是8个比特位,有符号的情况下最高位被当为符号位。

int main()
{
	char a = -160;
	//10000000000000000000000010100000-原码
	//11111111111111111111111101011111-反码
	//11111111111111111111111101100000-补码
	//char只能访问1个字节01100000
	//最高位被当为符号位,打印的是整形,前面补符号位
     //00000000000000000000000001100000
	printf("%d\n", a);
	return 0;
}

操作符(原码反码补码)_第30张图片  

       即使是有符号的整形,在整形提升时,也是按照最高位提升。

int main()
{
	char a = -1;
	signed char b = -1;
	//有符号数补码全为1,取1个字节
	//最高位为1,打印整形,前面补1,补码,换为原码为-1
	unsigned char c = -1;
	//无符号,-1补码还是全1,取1个字节
	//虽然最高位是1,但是无符号,前面补0
	printf("a=%d,b=%d,c=%d", a, b, c);
	return 0;
}

操作符(原码反码补码)_第31张图片

练习巩固: 

       其实和我们学的数学的加减乘除一样,这些操作符也是有优先级的。而且像读文章一样,我们是从左向右去读文章的,所以计算也大多是从左向右开始计算的,这称之为操作符的结合性。

       像有一些垃圾书籍总喜欢在这上面大做文章,出类似以下的出生代码:

操作符(原码反码补码)_第32张图片

        我们可以看到在各个编译器下结果不同,因为你无法确定执行的--和++哪一次结果是保留的,下面来看一些正常的练习。 

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
	printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
	return 0;
}

操作符(原码反码补码)_第33张图片

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ || ++b || d++;
	printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
	return 0;
}

操作符(原码反码补码)_第34张图片

       最后,我们给出每个操作符的优先级顺序:操作符(原码反码补码)_第35张图片 

操作符(原码反码补码)_第36张图片

操作符(原码反码补码)_第37张图片

操作符(原码反码补码)_第38张图片

你可能感兴趣的:(c语言)