【C语言】操作符详解(深入理解+整理归类)

💖 技术宅,拯救世界!

🎁 对读者的话:相信奇迹的人本身和奇迹一样伟大

在这里插入图片描述

生命中最快乐的事情是拼搏,而非成功,生命中最痛苦的是懒散,而非失败。大家好,这里是ecember。今天我们来介绍一下C语言中的所有操作符,在本篇博客中ecember将其整理归类了一波(以下结果均在VS2022中编译)。


🌹感谢大家的点赞关注 🌹,如果有需要可以看我主页专栏哟💖


⚡1. 二进制操作符

我们C语言中有一系列操作符。如下:

算术操作符 | 移位操作符 | 位操作符 | 赋值操作符 | 单目操作符
关系操作符 | 逻辑操作符 | 条件操作符 | 逗号表达式 | 下标引用、函数调用和结构成员

下面我将为大家一一介绍。

🌠 1.1 算术操作符

在这里插入图片描述

注意:
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

🌠 1.2 移位操作符

在这里插入图片描述
小科普
计算机中的整数有三种2进制表示方法,即原码、反码和补码。三种表示方法均有符号位数值位两部分,符号位都是用0表示“正”,用1表示“负”,对于我们的数值位:

<1> 正数的原、反、补码都相同.
<2>负整数的三种表示方法各不相同
1.原码: 直接将数值按照正负数的形式翻译成二进制就可以得到原码。
2.反码: 将原码的符号位不变,其他位依次按位取反就可以得到反码。
3.补码: 反码+1就得到补码。

对于整数来说,内存中存放的都是补码。为什么呢?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。总之,补码更方便计算
例一:
5
二进制-101-一个int位-4个字节-32个bit位
+5
000000000000000000000101-原码
000000000000000000000101
000000000000000000000101
-5
100000000000000000000101-只变符号位
111111111111111111111010-原码符号不变,其它位按位取反得到的即为反码
111111111111111111111011-反码+1即为补码

🌀1.2.1 左移操作符

我们的左移操作符实际上针对的是整数的二进制位形式。

移位规则:左边抛弃、右边补0

在这里插入图片描述

🌀1.2.2 右移操作符

移位规则:首先右移运算分两种:
1. 逻辑移位
左边用0填充,右边丢弃
2. 算术移位
左边用原该值的符号位填充,右边丢弃

在这里插入图片描述
注意对于右移操作符,不要移动负数位,这个是标准未定义的。

🌀1.2.3 实例解析

int main()
{
	int a = -7;
	int b = a << 1;//<< >> 操作的二进制位

	printf("%d\n", a);
	printf("%d\n", b);

	return 0;
}

在这里插入图片描述

int main()
{
	int a = -5;
	int b = a >> 1;

	printf("%d\n", a);
	printf("%d\n", b);

	return 0;
}

在这里插入图片描述
这里给大家总结了一条规律。

<< ——左移1位* 2
>> ——右移1位 / 2
取整方式:地板取整

注意:右移操作是算数右移还是逻辑右移取决于编译器。

🌠 1.3 位操作符

在这里插入图片描述
位操作符跟前几个一样是,也是操作的整数的二进制位

🌀1.3.1 按位与 & 按位或 |

我们先来试试手。

int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;//二进制位相同且为 1 才为 1 ,其它情况全为 0
	int d = a | b;//二进制位相同且为 0 才为 0 ,其它情况全为 1

	printf("%d\n", c);
	printf("%d\n", d);

	//00000000000000000000000000000011 3的补码
	//11111111111111111111111111111011 -5的补码
	
	//00000000000000000000000000000011 按位与得到正数(三码相同)
	//11111111111111111111111111111011 按位或得到负数(补码需转换)
	
	//转换如下
	//10000000000000000000000000000101 -原码
	//11111111111111111111111111111010 -反码
	//11111111111111111111111111111011 -补码


	return 0;
}

🌀1.3.2 按位异或

我们先来看看一组简单的例子。

int main()
{
	int a = 3;
	int b = -5;
	int e = a ^ b;

	printf("%d\n", e);
	//按位异或 ——相同为0,相异为1

	//11111111111111111111111111111000 按位异或(比较特殊得到 - 8)

	return 0;
}

值得注意的是下面这种。

//a ^ a = 0
//0 ^ a = a

//0000
//0101
//0101-a

我们可以利用这个来实现很多需要的代码。

🌀1.3.3 实例解析

1
既然我们学了上述不常见的操作数。我们现在来看一组变态的面试题。

不能创建临时变量(第三个变量),实现两个数的交换。

int main()
{
	int a = 3;
	int b = 5;
	printf("a=%d b=%d\n", a, b);
	
    //法一 创建临时变量
	/*int temp = a;
	a = b;
	b = temp;*/
    
    //法二 用 + - 运算
	/*a = a + b;
	b = a - b;
	a = a - b;*/
	
	//法三 异或
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;

	printf("a=%d b=%d\n", a, b);

	return 0;
}

这里我给出了三种方法,这里我们主要讲第三种。我们先来看看运行结果。
在这里插入图片描述
我们成功实现了值的交换,那么原理又是怎样的呢?我们上一小节讲了a ^ a = 0,a ^ 0 = a,我们这里把第一个表达式代入第二个表达式即 b = a ^ b ^ b,嘿嘿直接代入公式实现b = a;第三个表达式也是一样的原理。

2
我们再给出一道有意思的题目。

编写代码实现:求一个整数存储在内存中的二进制中1的个数。

//方法一
int main()
{
	int num = 10;
	int count = 0;//计数
	while (num)
	{
		if (num % 2 == 1)
			count++;
		num = num / 2;
	}
	printf("二进制中1的个数 = %d\n", count);
	return 0;
}

这种方法比较常见,但是有bug,负数的时候就不成立了。

//方法二
int main()
{
	int num = -1;
	int i = 0;
	int count = 0;//计数
	for (i = 0; i < 32; i++)
	{
		if (num & (1 << i))
			count++;
	}
	printf("二进制中1的个数 = %d\n", count);
	return 0;
}

这种方法就完美解决了上述问题,而且代码显得比较高级。但是循环次数比较多,使得此算法效率相对来说较低。

//方法三
int main()
{
	int num = -1;
	int i = 0;
	int count = 0;//计数
	while (num)
	{
		count++;
		num = num & (num - 1);
	}
	printf("二进制中1的个数 = %d\n", count);
	return 0;
}

我们老老实实举几个例子来算一下就会发现此方法的巧妙之处:自动过滤0,直接每次跳过一个1,这样就大大减少了我们的循环次数。

⚡2. 赋值操作符

也就是我们熟悉的 ’ = '了。赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。

int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值

🌠 2.1 赋值操作符合规使用

赋值操作符可以连续使用,比如

int a = 10;
int x = 0;
int y = 20; a = x = y+1;//连续赋值

但是我们感觉这样的代码风格怎么样?是不是感觉很挫啊哈哈。

x = y+1; 
a = x;

这样的写法是不是更加清晰爽朗而且易于调试。

🌠 2.2 复合赋值符

在这里插入图片描述
我们上述的操作符都可以和 = 组成复合赋值符来使用。

int x = 10; x = x+10; x += 10;//复合赋值

分开写一样的道理,但这样写更加简洁。

⚡3. 单目操作符

在这里插入图片描述
由于部分操作符在我们的 初识C语言——下篇已经详细讲过了,此处不再赘述。这里我们主要帮大家回忆一下部分。

int main()
{
 int a = -10;
 int *p = NULL;
 printf("%d\n", !2);
 printf("%d\n", !0);
 a = -a;
 p = &a;
 printf("%d\n", sizeof(a));
 printf("%d\n", sizeof(int));
 printf("%d\n", sizeof a);//可以这样写
 printf("%d\n", sizeof int);//错误写法
 
 return 0; 
 }

用sizeof计算单独的变量可以不加括号,但计算类型必须加上括号。

void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(2)
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//(4)
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//(1)
	printf("%d\n", sizeof(ch));//(3)
	test1(arr);
	test2(ch);
	return 0;
}

下列式子中(1)(2)(3)(4)分别输出什么?
在这里插入图片描述
main函数内部的计算就是计算整个数组的大小,所以40和10没有问题,而我们知道当把一个文数组传给函数时,实际上就是传递数组首元素的地址,而地址我们需要用指针来接收,所以函数内部实际上是计算指针所占的字节,在x64环境下自然是8。

我们再来看看一段代码,看看输出什么?

int main()
{
	short  a = 10;
	short s = 0;
	printf("%zu\n", sizeof(s = a + 2));
	printf("%d\n", s);

	return 0;
}

在这里插入图片描述
这个2比较好理解,因为short所占的字节数就是2,那么0是为什么呢?我们明明对s进行赋值运算了呀,所以我们需要了解:sizeof括号内部表达式不参与计算_是在编译期间处理的。

⚡4. 关系操作符

在这里插入图片描述
想必这些操作符大家应该都耳熟于心了吧,但是值得注意的是 千万不要把 ‘==’ 跟‘=’搞混了,很多时候程序有错,但就是找不出来,那么大家可以看看判断相等的时候是不是写成了赋值。

小科普

我们比较整型等类型的时候用的是’==’ ,那么比较字符串可不能用这个,我们这里介绍一个函数 strcmp,我们在cplusplus上稍微看一下简介。
在这里插入图片描述
得知此函数可以来比较俩字符串是否相等,特别要注意判断字符串大小的时候:>
strcmp函数是用来比较字符串大小的,是比较对应位置上字符的大小一旦遇到不相等的就比较,比较后终止,不是比较长度!!!

⚡5. 逻辑操作符

在这里插入图片描述

注意要区分:
区分逻辑与按位与
区分逻辑或按位或

🌠 5.1 基本使用

一图解决。
在这里插入图片描述

🌠 5.2 360笔试题

#include <stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;//<1>
	//i = a++||++b||d++;//<2>

	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
	return 0;
}
//程序输出的结果是什么?

<1>

在这里插入图片描述
这里可能许多小伙伴会有很多疑惑,这到底是为什么呢?我们一步一步来a++ && ++b && d++

<1> 先a++,由于是后置++,所以最后再++;a = 1
<2> b++,后置++; b = 3
<3> d++,后置++;d = 4

但我们发现结果尽不如人意,只有a ,c的结果相同,我们观察结果得知b和d根本没有参与运算,所以:编译器为了方便,&&左边为假右边不用参与运算,右边直接相当于默认没有。

<2>
当我们运行第二个表达式时,我们套用一的经验可得:||左边为真,右边直接不用参与运算
我们不妨来验证验证。照这个逻辑是1 3 3 4。
在这里插入图片描述

⚡6. 多目操作符

这里我们主要讲条件操作符和逗号表达式。

🌠 6.1 条件操作符

在这里插入图片描述
我们先来想一想如果要求两数之间较大的那个数,是不是要用条件语句来实现。

if (a > 5)
        b = 3;
else
        b = -3;

此式子可以转化为条件表达式。

(a > 5)? (b = 3, b = -3)

🌠 6.2 逗号表达式

在这里插入图片描述
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。我们来浅看几个例子。

int main(
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
//c是多少?


//代码2
if (a =b + 1, c=a / 2, d > 0)


//代码3 
a = get_val();
count_val(a);
while (a > 0) 
{
        a = get_val();
        count_val(a);
}
//如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0) { ;}
 

⚡7. 下标引用,函数调用和结构成员

我们这里主要了解一下[ ],(),* 等。

🌠 7.1 下标引用操作符 [ ]

操作数:一个数组名 + 一个索引值。如下:

int arr[10];//创建数组
 arr[9] = 10;//实用下标引用操作符。
// [ ]的两个操作数是arr和9。

🌠 7.2 函数调用操作符 ( )

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

#include <stdio.h>
 void test1()
 {
 printf("hehe\n");
 }
 void test2(const char *str)
 {
 printf("%s\n", str);
 }
 int main()
 {
 test1();            //实用()作为函数调用操作符。
 test2("hello bit.");//实用()作为函数调用操作符。
 
 return 0;
 }

🌠 7.3 访问一个结构的成员

在这里插入图片描述

#include <stdio.h>
struct Stu
{
 char name[10];
 int age;
 char sex[5];
 double score;
}void set_age1(struct Stu stu) 
{
 stu.age = 18; 
 }
 
void set_age2(struct Stu* pStu) 
{
 pStu->age = 18;//结构成员访问
}

int main()
{
 struct Stu stu;
 struct Stu* pStu = &stu;//结构成员访问
 
 stu.age = 20;//结构成员访问
 set_age1(stu);
 
 pStu->age = 20;//结构成员访问
 set_age2(pStu);
 
 return 0; 
 }

⚡8. 结语

到这,我们的 《操作符详解》 已经接近尾声了,后续我还会持续更新C语言相关内容,学习永无止境,就会永远只会留给有准备的人。希望我的博客对大家有所帮助,如果喜欢的可以 点赞+收藏哦,也随时欢迎大家在评论区及时指出我的错误。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

guaabd

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

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

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

打赏作者

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

抵扣说明:

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

余额充值