C语言操作符(上)

操作符

一,操作符的分类

C语言中提供的操作符有一下这么几种:

• 算术操作符: + 、- 、* 、/ 、%

• 移位操作符: << >>

• 位操作符: & | ^

• 赋值操作符: = 、+= 、 -= 、 = 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^=
• 单⽬操作符: !、++、--、&、
、+、-、~ 、sizeof、(类型)

• 关系操作符: > 、>= 、< 、<= 、 == 、 !=

• 逻辑操作符: && 、||

• 条件操作符: ? :

• 逗号表达式: ,

• 下标引⽤: []

• 函数调⽤: ()

• 结构成员访问: . 、->
看到这些操作符相信有一些是你已经见过并熟练掌握的,但还有一些涉及到位运算的操作符你可能是初次见到。像移位操作符,逗号表达式,位操作符等,本篇文章重点来讲述涉及到位的操作符。

我们先来花小部分篇幅来讲算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单⽬操作符。

1,算数操作符

算术操作符有这几种分别为: + 、- 、* 、/ 、%唯一需要注意的就是取模运算,计算结果得到的是余数。
通过重复除10模10(/10%10)这两个算数运算符可以获得一个多位数的每一位!

2,赋值操作符

赋值操作符有以下几种分别为:= 、+= 、 -= 、 *= 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^= 。这种写法其实是n=n+m简写成n+=m的写法。赋值运算符要和关系运算符分开,很多初学者搞不清一个等号和两个等号。
一个等号 = -> 赋值
两个等号 ==-> 判断相等

其中包含的<<,>>,&,^是稍后要介绍的设计位的操作符。

3,逻辑操作符

逻辑操作符有两种: && 、||。&&叫逻辑与 ||叫逻辑或

逻辑操作符主要关心的是布尔值,布尔值True表示真,False表示假,而计算机是二进制只有0和1来表示所以1表示真,0表示假。

所以逻辑操作符在运算的时候有几种情况: 0&&0为0 1&&0为0 1||0为1 0||0为0

在学逻辑操作符还要记住一句话,逻辑与有假为假,逻辑或有真为真,非零即真。

4,条件操作符

条件操作符主要为: ? : 。这个操作符具体形式为:表达式?结果一:结果二。

举个简单的例子:

c 复制代码
int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	c = a > b ? a : b;
	printf("%d", c);
	printf("\n");
	return 0;
}

从运行结果来看得到的值为5,那为什么是5呢/我们来分析一下代码:

a>b?a:b 从左到右?意思是表达式左边的值为真吗?也就是a是大于b的吗?如果大于结果为a,再将a赋值给c,如果小于,结果为b,再将b赋值给a。

总结:

所以 表达式?结果一:结果二表达式为真,执行结果一;表达式为假,执行结果二。

4,单目操作符

单⽬操作符有以下几种: !、++、--、&、*、+、-、~ 、sizeof、(类型)。其中也有一些位操作符,我们稍后介绍。还有一些像自增,自减已经在之前就介绍过。其中比较重要的就是sizeof和~取反操作。

1. sizeof是用来计算类型大小的,比如sizeof(int/short/long/double)。sizeof也经常用来计算数组元素的个数。

**2. ~取反操作,就是将以二进制形式存储的数据进行取反操作。**这点等介绍完了原码反码补码之后你就会明白了。
3,还要注意的是如果sizeof里面有表达式是不参与计算的,因为sizeof在编译阶段就直接返回类型的大小了,比如int类型大小为4。而表达式计算是在运行时才计算的。

5,函数调用和下表访问操作符

函数调用和下标访问操作符在之前的文章有讲到过,可以参考之前的文章。

传送门函数:函数

传送门数组:数组

二,原码反码补码

在介绍设计位操作符之前我门先要了解数据在计算机的存储形式,上一篇文章我们详细介绍了进制转换,有了进制转换的知识就为我们下面的学习打下了基础。如果还不了解进制转换可以去看上一篇文章:传送门。

在上一篇文章中我们介绍了计算机的底层实际上就是电路,而电路只有高电平和低电平两种状态这就与二进制相似只有0和1这两种状态。这时可能有人问计算机是怎么存储数据的呢?比如在我们写代码的时候我们通常会定义一个变量然后给它赋值,那这个值是怎样存储的呢?

首先来介绍一下什么是原码,反码,补码:

1. 原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
2. 反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
3. 补码:反码+1就得到补码。

整数的2进制表示方法有三种,即原码、反码和补码 有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号 位,剩余的都是数值位。 符号位都是⽤0表⽰"正",⽤1表⽰"负"。一个整型数是占4个字节 一个字节占8比特位 所以是由32位二进制来构成 举个例子:

而整数又分为有符号整数和无符号整数这两种,而有符号数又分为正整数和负整数这两种。

  • 正整数的原、反、补码都相同。
  • 负整数的三种表方法各不相同。
  • 负整数取反加一为补码 补码取反加一为原码。
    我们来看一个简单的运算:
c 复制代码
#include<stdio.h>
int main()
{
	int a = 10;
	int b = -5;
	//10的原码00000000000000000000000000001010
	//因为10为整数 原码=反码=补码
	//-5的原码10000000000000000000000000000101
	//-5的反码11111111111111111111111111111010
	//-5的补码11111111111111111111111111111011
	int c=a+b;
	//a+b 是补码运算
	//10的补码       00000000000000000000000000001010
	//-5的补码       11111111111111111111111111111011
	//相加之后的补码:100000000000000000000000000000101 最左边多出的一位将丢弃掉所以符号位为0是正整数 原反补都相同,直接读出101对应的值是5
	printf("%d",c);
	return 0;
}


由此我们可以总结:

但是数据存放内存中其实存放的是补码这是为什么呢?

在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀ 处理;同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算 过程是相同的,不需要额外的硬件电路。

三,移位操作符

1,左移操作符

移位规则:左边抛弃、右边补0;来看一段代码:

c 复制代码
#include<stdio.h>
int main()
{
	int num=10;
	int m = num<<1;
	printf("num = %d",num);
	printf("m = %d",m);
	return 0;
}

从结果上看不知各位读者看出来了吗就是左移是不会改变原来的值。我们给出具体分析

2,右移操作符

移位规则:⾸先右移运算分两种:

  1. 逻辑右移:左边⽤0填充,右边丢弃
  2. 算术右移:左边⽤原该值的符号位填充,右边丢弃
    一般情况下都是算数右移动。下面来看代码;
c 复制代码
#include <stdio.h> 
 int main() 
 { 
 int num = 10; 
 int n = num>>1; 
 printf("n= %d\n", n); 
 printf("num= %d\n", num); 
 return 0; 
 } 
//逻辑右移1位演⽰ 算术右移1位演⽰ 
//警告:对于移位运算符,不要移动负数位,
//这个是标准未//定义的。 例如:1 int num = 10;

我们分别给出逻辑右移和算数右移:

总结:
移位操作符操作数只能是整数!不能是其它的数!

四,位操作符

位操作符有三种分别是: &(按位与) |(按位或) ^(按位异或)~(按位取反)。其中按位的位指的是二进制位。举个例子;

c 复制代码
#include<stdio.h>
int main()
{
	int a = -3;
	int b = 6;
	int x = 0;
	int y = 0;
	int r = 0;
	int c = 0;
	x = a & b;
	y = a|b;
	r = a^b;
	c = ~a;
	printf("a&b = %d\n",x);
	printf("a|b = %d\n",y);
	printf("a^b = %d\n",r);
	printf("~a = %d\n",c);
	return 0;
}

紧接着我们来分析为什么是这个结果。大家请看图

通过图片已经能非常直观的能看清楚每一步的运算过程了。但有一点要注意就是按位操作符他们的操作数必须是整数。

学习完上面的知识后我们来看一道比较变态的面试题,题目是不创建第三变量怎样实现两个数的交换。这里我们不卖关子直接给出代码然后分析:

c 复制代码
#include<stdio.h>
int main()
{
	int a = 3;
	int b = 6;
	printf("交换前a=%d b=%d\n",a,b);
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("交换后a=%d b=%d",a,b);
	return 0;
}

是不是很神奇,那为什么这样就能直接交换了呢?我们给出相应的图解;

紧接着我们扩展学习一些按位操作符的妙用。

1,&按位与的妙用

我们用一道编程题来举例,求⼀个整数存储在内存中的⼆进制中1的个数。(一下都以这道题为例)我们直接给出代码:

c 复制代码
#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d",&n);
	int count = 0;
	while(n)
	{
		if(n%2==1)
		{
			count++;
		}
		n=n/2;
	}
	printf("count=%d",count);
	return 0;
}


15的二进制序列为00000000000000000000000000001111 写出其补码后发现有4个一这与结果是吻合的,但当我们输入负一结果就不对了显然这种方法有缺陷。我们给出改进方法:

c 复制代码
#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	for (int i = 0;i < 32;i++)
	{
	
		if (n&1==1)
		{
			count++;
		}
		n = n >> i;
	}
	printf("count=%d", count);
	return 0;
}

这时候我们来看就会发现已经得到我们想要的结果了,按位与是不是非常的神奇呢我们来分析一下原理。

还有一个更加巧妙的方法,我们直接给出代码让你体会体会这种巧妙:

c 复制代码
#include<stdio.h>
int main()
{
	int num = 0;
	scanf("%d", &num);
	int count = 0;
	while (num)
	{
		num = num & (num - 1);
		count++;
	}
	printf("count=%d\n", count);
	return 0;
}

这段代码明显又要比上面的代码更加高效了,上面无论怎样都要执行32次循环,而下面这段代码有几个1就循环几次效率大幅提升。那为什么是这样呢?我们画个图分析一下;

2,|按位或的妙用

先来看一道题目,将13二进制序列的第5位修改为1,然后再改回0;

先画个图分析:

我们给出代码:

c 复制代码
#include<stdio.h>
int main()
{
	int m = 13;
	int n = 5;
	m = m|(1 << (n - 1));
	printf("%d", m);
	return 0;
}


这里如果为什么变成了29还不知道的话那就要去看一下我之前的文章进制转换啦传送门:
那怎么换回来呢?其实也简单,我们画图来分析:


我们给出代码:

c 复制代码
#include<stdio.h>
int main()
{
	int m = 13;
	int n = 5;
	m = m|(1 << (n - 1));
	printf("%d", m);
	m=m&~(1 << (n - 1));
	printf("%d",m);
	return 0;
}

由于文章篇幅有限以上就是本篇文章的所有内容啦,还有些内容将在下期发布敬请期待!

文章有点长感谢能够看到这里的读者,给你们点赞!如果这篇文章能够帮到你,制作不易还请各位读者给一个三连!你们的支持就是我最大的动力!

相关推荐
장숙혜10 分钟前
JavaScript正则表达式解析:模式、方法与实战案例
开发语言·javascript·正则表达式
安大小万27 分钟前
C++ 学习:深入理解 Linux 系统中的冯诺依曼架构
linux·开发语言·c++
随心Coding31 分钟前
【零基础入门Go语言】错误处理:如何更优雅地处理程序异常和错误
开发语言·后端·golang
T.Ree.35 分钟前
C语言_自定义类型(结构体,枚举,联合)
c语言·开发语言
Channing Lewis36 分钟前
python生成随机字符串
服务器·开发语言·python
小熊科研路(同名GZH)1 小时前
【Matlab高端绘图SCI绘图模板】第002期 绘制面积图
开发语言·matlab
鱼是一只鱼啊1 小时前
.netframeworke4.6.2升级.net8问题处理
开发语言·.net·.net8
Tanecious.1 小时前
C语言--数据在内存中的存储
c语言·开发语言·算法
咸甜适中2 小时前
go语言gui窗口应用之fyne框架-动态添加、删除一行控件(逐行注释)
开发语言·后端·golang