初阶C语言-操作符详解(上)

“不飞则已,一飞冲天;不鸣则已,一鸣惊人。” 今天,我们一起来学习一下操作符的相关知识。

1.操作符分类

算术操作符 + - * / %
移位操作符 << >>
位操作符 & | ^
赋值操作符 = += -= *= /=...
单目操作符 ! sizeof ++ -- ...
关系操作符 > >= < <= == !=
逻辑操作符 && ||
条件操作符 ? :
逗号表达式 ,
下标引用、函数调用和结构成员 [] () ->

2.算术操作符

+ - * / %

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

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
	int m = 7 / 2;
	printf("%dn", m);
	double n = 7.0 / 2;
	printf("%lfn", n);
	int o = 7 % 2;
	printf("%dn", o);
	return 0;
}

在这里插入图片描述

3.移位操作符

>>右移操作符 <<左移操作符

注:移位操作符的操作数只能是整数。

对于一个整数,是4个字节 = 32个比特位。一个整数写成二进制序列的时候,就是32个比特位。对于有符号的整数来说,最高位为符号位,符号位是1表示负数,符号位是0表示正数。对于无符号数整数来说,没有符号位,所有位都是有效位。

整数的二进制表示形式有3种:原码、反码、补码。
原码:按照数值的正负,直接写出的二进制序列就是原码。

例如:10这个数的原码就是00000000000000000000000000001010,那么-10的原码就是10000000000000000000000000001010
注:对于正的整数,原码、反码、补码相同,无需计算。
对于负的整数,原码、反码、补码是需要计算的。

反码:原码的符号位不变,其他位按位取反。(按位取反,原来是0的改为1,原来是1的改为0)
补码:反码的二进制+1就得到补码。

因此,10这个数的原码、反码、补码都是00000000000000000000000000001010-10的原码就是10000000000000000000000000001010,反码是11111111111111111111111111110101,补码是11111111111111111111111111110110整数在内存中存储的都是补码的二进制序列。 整数在计算的时候,使用的也是补码。

3.1左移位操作符

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

#include <stdio.h>
int main()
{
	//m:00000000000000000000000000000111
	int m = 7;
	int n = m << 1;
	//左移位原则:左边抛弃,右边补0
	//n:00000000000000000000000000001110
	//n = 2+4+8=14
	printf("%dn", m);
	printf("%dn", n);
	return 0;
}

在这里插入图片描述
我们可以发现左移位操作符可以起到加倍的作用。负数也是一样的道理:
在这里插入图片描述

3.2右移操作符

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

//右移位操作符
#include <stdio.h>
int main()
{
	//a的原码:10000000000000000000000000001010
	//a的反码:11111111111111111111111111110101
	//a的补码:11111111111111111111111111110110
	//大多数编译器采用的是算术右移
	//b是算术右移(右边直接丢弃,左边补原符号位,原来是负数,左边补1,原来是正数,左边补0)
	//b的补码:11111111111111111111111111111011
	//b的反码:11111111111111111111111111111010
	//b的原码:10000000000000000000000000000101
	//b=-(1+4)=-5
	int a = -10;
	int b = a >> 1;
	printf("a=%dn", a);
	printf("b=%dn", b);
	return 0;
}

在这里插入图片描述
我们可以看到,算术右移也起到了/2的作用。
注:对于移位操作符,不要移动负数位,这个是标准未定义的。

4.位操作符

位操作符有:|(按位或)&(按位与)^(按位异或)

注:他们的操作数必须是整数。

4.1按位与&

对于两个数a和b,用按位与运算符求c = a&b;,需要先写出a和b的二进制位的补码形式,然后一一对比,只有当两个数a和b的对应的补码都为1的时候,按位与的结果才是1,否则为0。

#include <stdio.h>
int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;//按(二进制)位与
	//-5的原码:10000000000000000000000000000101
	//-5的反码:10000000000000000000000000000100
	//-5的补码:11111111111111111111111111111011
	// 3的补码: 00000000000000000000000000000011
	// 按位与:两个都为1才为1
	//       c: 00000000000000000000000000000011
	//结果为3
	printf("%dn", c);
	return 0;
}

在这里插入图片描述

4.2按位或|

对于两个数a和b,用按位或运算符求c = a|b;,需要先写出a和b的二进制位的补码形式,然后一一对比,只要当两个数a和b的对应的补码有一个为1的时候,按位或的结果就是1,否则为0。

#include <stdio.h>
int main()
{
	int a = 3;
	int b = -5;
	int c = a | b;//按(二进制)位或
	//-5的原码:10000000000000000000000000000101
	//-5的反码:10000000000000000000000000000100
	//-5的补码:11111111111111111111111111111011
	// 3的补码: 00000000000000000000000000000011
	// 按位或:有一个为1即为1
	// c的补码: 11111111111111111111111111111011
	// c的反码: 11111111111111111111111111111010
	// c的原码: 10000000000000000000000000000101
	//结果为-5
	printf("%dn", c);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

4.3按位异或^

对于两个数a和b,用按位或运算符求c = a^b;,需要先写出a和b的二进制位的补码形式,然后一一对比,两个数a和b的对应的补码相同为0,不同为1.

#include <stdio.h>
int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b;//按(二进制)位异或
	//-5的原码:10000000000000000000000000000101
	//-5的反码:10000000000000000000000000000100
	//-5的补码:11111111111111111111111111111011
	// 3的补码: 00000000000000000000000000000011
	// 按位异或:相同为0不同为1
	// c的补码: 11111111111111111111111111111000
	// c的反码: 11111111111111111111111111110111
	// c的原码: 10000000000000000000000000001000
	//结果为-8
	printf("%dn", c);
	return 0;
}

在这里插入图片描述
注:特别地,a^a=0; a^0=a;.

下面,我们给出一个特别的题:在不创建临时变量的情况下(没有第三个变量),实现两个数的交换。

通过前面的学习,我们知道在使用第三个变量是怎么实现的了:

//实现两个数的交换
#include <stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	printf("交换前a=%d,b=%dn", a, b);
	c = a;
	a = b;
	b = c;
	printf("交换后a=%d,b=%dn",a,b);
	return 0;
}

在这里插入图片描述
那么,如果我们不使用第三个变量,该如何实现同样的功能呢?

//实现两个数的交换
#include <stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	printf("交换前a=%d,b=%dn", a, b);
	a = a + b;
	b = a - b;
	a = a - b;
	printf("交换后a=%d,b=%dn", a, b);
	return 0;
}

在这里插入图片描述
这里,我们只用了两个变量,但是还是存在缺陷,就是当a和b的值很大的时候,就会出现溢出的问题。这里,我们就需要用到异或^这个操作符了。

//实现两个数的交换
#include <stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	printf("交换前a=%d,b=%dn", a, b);
	a = a ^ b;//a=3^5;
	b = a ^ b;//b=3^5^5=3
	a = a ^ b;//a=3^5^3=3^3^5=5
	//异或支持交换律
	printf("交换后a=%d,b=%dn", a, b);
	return 0;
}

在这里插入图片描述
注:异或^不存在溢出的问题,但是可读性不高,只限于整型的运算。

5.赋值操作符

赋值操作符用于在变量创建好之后对值的修改。

#include <stdio.h>
int main()
{
	int a = 3;//不是赋值,是初始化
	a = 10;//赋值
	return 0;
}

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

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = a = a + b;//连续赋值
	//连续赋值从右往左算
	//a = a + b = 30
	//c = a = 30
	printf("%dn", c);
	return 0;
}

在这里插入图片描述
但是,连续赋值的语句不利于调试,上述的int c = a = a + b;可以拆分成:

a = a + b;
c = a;
//这样的写法更清晰爽朗且易于调试。

复合赋值符:+= -= *= /= %= >>= <<= &= |= ^=

这些运算符都可以写成复合的效果,例如:

int a = 2;
//以下两种写法表达含义一致
a = a + 10;
a += 10;

6.单目操作符

6.1单目操作符的介绍

单目操作符 名称
逻辑反操作符
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
- - 前置、后置- -
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换

1.逻辑反操作符(!)

//0表示假,1表示真
#include <stdio.h>
int main()
{
	int a = 1;
	int b = 0;
	b = !a;
	printf("%dn", b);
	return 0;
}

在这里插入图片描述
2.解引用操作符(*)

//解引用操作符*
#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	*p;//对p解引用操作,*p是通过p中存放的地址,找到p指向的对象
	//*p就是a
	return 0;
}

3.sizeof和数组

//sizeof计算的是类型创建变量或变量的大小,单位是字节
//sizeof计算的结果是size_t类型的
//size_t是无符号的
//对size_t类型的数据进行打印,可以用%zd
//sizeof后面的括号在括号中写的不是类型的时候,括号可以省略,这样也说明了sizeof不是函数
//sizeof是操作符-单目操作符
#include <stdio.h>
int main()
{
	int a = 10;
	printf("%zdn", sizeof(a));
	printf("%zdn", sizeof a);
	printf("%zdn", sizeof(int));
	int arr[10] = { 0 };
	printf("%zdn", sizeof arr);
	printf("%zdn", sizeof(arr[1]));
	return 0;
}

在这里插入图片描述
4.~(对一个数的二进制按位取反)

#include <stdio.h>
int main()
{
	int a = 0;
	printf("%dn", ~a);
	//a的补码: 00000000000000000000000000000000
	// 按位取反,就是不论是不是符号位都取反
	//~a的补码:11111111111111111111111111111111
	//~a的反码:11111111111111111111111111111110
	//~a的原码:10000000000000000000000000000001
	// 符号位0为正 1为负
	//~a的值为-1
	return 0;
}

在这里插入图片描述
那么,接下来,可以看看~怎么用?

#include <stdio.h>
int main()
{
	int a = 10;
	//000000000000000000000000000001010
	//如果我们想让从右往左数第三个数变成1该怎么做?
	// 即变成00000000000000000000000000001110为14
	//将000000000000000000000000000000001左移二
	//1<<2  00000000000000000000000000000100
	//将左移后的二进制位与a的二进制位按位或即可
	a |= (1 << 2);
	printf("%dn", a);
	//那么,反过来,如果希望从右往左数第三个数再变为0该怎么办?
	// 只需要将下面第一个和第二个按位与就能得到第三个
	//0000000000000000000000000000001110
	//1111111111111111111111111111111011,这个可以将(1<<2)取反得到
	//0000000000000000000000000000001010
	a &= ~(1 << 2);
	printf("%dn", a);
	return 0;
}

在这里插入图片描述
5.++ --操作符

#include <stdio.h>
int main()
{
	int a = 2;
	//++a;
	//++是一种自增1的操作
	a++;
	//前置++和后置++对于a本身没有影响,都是进行+1的操作
	printf("%dn", a);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
前置++和后置++对于a本身没有影响但是对于别人有影响!示例如下:

#include <stdio.h>
int main()
{
	int a = 2;
	int b = a++;
	//后置++:先使用,后+1
	printf("%dn", a);
	printf("%dn", b);
	return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{
	int a = 2;
	int b = ++a;
	//前置++:先+1,后使用
	printf("%dn", a);
	printf("%dn", b);
	return 0;
}

在这里插入图片描述

前置--和后置--和前、后置++同理,大家可以自己试一下!

好啦,关于操作符的知识点就先讲到这里,后期会继续更新,欢迎大家持续关注、点赞和评论!