C语言操作符详解

目录

一. 操作符分类

二. 算数操作符

三. 逻辑操作符 

1.&&--逻辑与

 2.||---逻辑或

3. !----逻辑非

四. 移位操作符

1.进制的概念

1.1 2进制转10进制

1.2 2进制转换成8进制

1.3 2进制转换成16进制

2.原码、反码、补码 

3. << 左移操作符

4. >> 右移操作符

五.位操作符:&、|、^、~

1.&---按位与

2.|-----按位或

3. ^---按位异或

按位异或的特点:

4. ~ ---按位取反 

六. 逗号表达式

七. 下标访问[ ]、函数调⽤( )

7.1 下标引用操作符[ ]

7.2  函数调⽤操作符( )

八. 结构成员访问操作符

九. 操作符的属性:优先级、结合性 

1. 优先级

2 结合性


一. 操作符分类

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

• 移位操作符: <<  >>

• 位操作符: & 、|  、^

• 赋值操作符: = 、+= 、 -= 、 *= 、 /= 、%= 、>= 、&= 、|= 、^=

• 单⽬操作符: !、++、--、~ 、sizeof(类型)

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

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

• 逗号表达式: ,

• 下标引⽤: []

• 函数调⽤: () 

• 结构成员访问操作符: . 、->

二. 算数操作符

算数操作符就是用来进行运算的操作符

+  ------加

-   ------减

*   ------乘

/    ------除

%  ------取余

	int a = 10;
	int b = 5;
	printf("%d\n", a + b);//15
	printf("%d\n", a - b);//5
	printf("%d\n", a * b);//50
	printf("%d\n", a / b);//2

	printf("%d\n", a % b);//0
    //10/5=2,余数为0;所以a%b=0

三. 逻辑操作符 

逻辑操作符是用来连接表达式并且判断语句的真假(在C语言中0表示假,非0表示真)

逻辑操作符分为:
&& —— 逻辑 “与”

||    —— 逻辑 ”或“

!  —— 逻辑 ”非“

1.&&--逻辑与

&& 可以理解为并且的意思,(当两个语句都为真时才为真,否则为假),例如:

int main()
{
	int a = 10;
	int b = 5;
	if (a > 0 && b > 0)//如果a>0并且b>0,那么就打印hehe
	{
		printf("hehe\n");
	}
	return 0;
}

 2.||---逻辑或

  || 可以理解为或者的意思,(只要有一个语句为真,就为真,只有2个语句都为假时,才为假),例如:

int main()
{
	int a = 10;
	int b = -1;
	if (a > 0 || b > 0)如果a>0或者b>0,那么打印hehe
	{
		printf("hehe\n");
	}
	return 0;
}

3. !----逻辑非

逻辑取反,(真为假,假为真)例如:

​
int main()
{
	int a = 0;
	if (!a)//如果a为假,那么!a就为真,打印hehe
	{
		printf("hehe");
	}
	return 0;
}

​

四. 移位操作符

在讲解移位操作符之前给大家科普一些内容:进制的概念,原码、反码、补码的概念。

1.进制的概念

在生活中,我们最常见的就是10进制(满10进1),但是我们经常还能听到2进制、8进制、16进制 这样的讲法,2进制、8进制、10进制、16进制、这些都是数值的不同表⽰形式。我们看看数值15的各种进制的表⽰形式:

15的2进制:1111

15的8进制:17

15的10进制:15

15的16进制:F

在我们的计算机当中,计算机是使用二进制作为数字编码系统,所有数据都是以二进制形式存储和传输的。接下来我们重点讲解一下2进制:

10进制中满10进1 ,10进制的数字每⼀位都是0~9的数字组成, 其实⼆进制也是⼀样的 ,2进制中满2进1 ,2进制的数字每⼀位都是0~1的数字组成 ,例如 1001 就是⼆进制的数字。

1.1 2进制转10进制

2进制该如何转换成10进制呢,例如上面提到的15的2进制是1111

1111转换成10进制15

从右边往左边进行计算就是:
1*2的3次方+1*2的2次方+1*2的1次方+1*2的0次方

1*8+1*4+1*2+1*1=15

如果是1100呢?其实是一样的:

1*2的3次方+1*2的2次方+0*2的1次方+0*2的0次方

1*8+1*4+0*2+0*1=12

相信大家都学会了2进制如何转换成10进制了,那么反过来10进制怎么转换成2进制呢?

 15转换成2进制也是如此:

15/2=7余数为1

7/2=3余数为1

3/2=1余数为1

1/2=0余数为1

从下往上得到的余数就是1111,所以1111就是15的2进制表示方式

1.2 2进制转换成8进制

8进制的数字每⼀位是0~7组成的,从2进制序列中右边低位开始向左每3个2进制位会换算⼀ 个8进制位,剩余不够3个2进制位的直接换算。

因为0~7的数字,各⾃写成2进制,最多有3个2进制位就⾜够了

如:2进制的 01 101 011,换成8进制:0153,0开头的数字,表示该数字为8进制

 011

0*2的2次方+1*2的1次方+1*2的0次方

0*4+1*2+1*1=3

101

1*2的2次方+0*2的1次方+1*2的0次方

1*4+0*2+1*1=5

01

0*2的1次方+1*2的0次方

0*2+1*1=1

1.3 2进制转换成16进制

16进制的数字每⼀位是0~9, a~f(10-15) 组成的,f 的⼆进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进 制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。

因为0~9, a~f (10-15) 的数字,各⾃写成2进制,最多有4个2进制位就⾜够了 

例如2进制的 0110 1011,换成16进制:0x6b,0x开头的数字表示该数字为16进制

1011

1*2的3次方+0*2的2次方+1*2的1次方+1*2的0次方

1*8+0*4+1*2+1*1=11(b)

0110

0*2的3次方+1*2的2次方+1*2的1次方+0*2的0次方

0*8+1*4+1*2+0*1=6

2.原码、反码、补码 

整数分为有符号整数(signed int )和无符号整数(unsigned int ),而整数的2进制表⽰⽅法有三种,即原码、反码和补码。有符号整数的三种表⽰⽅法均有符号位数值位两部分,2进制序列中,最⾼位的1位是被当做符号位,剩余的都是数值位。 符号位都是⽤0表⽰“正”,⽤1表⽰“负”。

正整数的原、反、补码都相同 

负整数的三种表⽰⽅法各不相同

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码

补码:反码+1就得到补码。

补码得到原码也是可以使⽤:取反,+1的操作。

在计算机中,对于整形计算是用补码进行计算的,计算完成后以原码的形式呈现

例如:

​
int main()
{
	int a = 8;
	int b = -4;
	int c = a + b;
	printf("%d", c);
	return 0;
}

​

b的2进制原码:10000000 00000000 00000000 00000100

b的2进制反码:11111111   11111111   11111111   11111011

b的2进制补码:11111111   11111111   11111111   11111100

a的2进制补码:00000000 00000000 00000000 00001000(正整数原码,反码,补码相同)

相加后得到:100000000 00000000 00000000 00000100(满2进1)我们保留32位(整形是4个字节=32bit位)忽略溢出的最高位,即得到:00000000 00000000 00000000 00000100

发现符号位为0,正整数的原码,反码,补码相同,所以直接转换成10进制就是4

 那么为什么整形数据存放在内存中其实存放的是补码呢?

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

3. << 左移操作符

左移操作符的移位规则:左边抛弃、右边补0(移位移的也是补码)

例如:

​
int main()
{
 int num = 10;
 int n = num<<1;// <<右边数字是多少,就移动多少位
 printf("n= %d\n", n);
 printf("num= %d\n", num);
 return 0;
}

​

如果想让num的值一起发生改变,将代码中的 int n=num<<1改成 int n=num<<=1即可,使左移操作符变成一种赋值操作符。

4. >> 右移操作符

右移操作符的移位规则分为2种:

1. 逻辑右移:左边⽤0填充,右边丢弃

2. 算术右移:左边⽤原该值的符号位填充,右边丢弃

使用哪种移位规则,取决于编译器,大部分编译器采用的都是算数右移

int main()
{
	int num = -10;
	int n = num >> 1;
	printf("n= %d\n", n);
	printf("num= %d\n", num);
	return 0;
}

逻辑右移一位演示:

算数右移一位演示:

左移操作符和右移操作符的特点:通过上面例子我们可以发现 一个数左移一位后的值等于原来的值*2,一个数右移后的值等于原来的值/2。

注意:对于移位运算符,不要移动负数位,这个是标准未定义的,移位操作符的操作数只能是整数,并且移动的范围应该在0-32之间,因为整形的2进制位最多为32位bit。

五.位操作符:&、|、^、~

位操作符中的位指的是2进制位,也是对2进制中的补码进行操作的。

1.&---按位与

与运算的规则: 2个操作数补码中的2进制位一 一对应,有0则为0,全部为1则为1

例如:

int main()
{
	int a = 10;
	int b = 8;
	int c = a & b;
	printf("%d", c);
	return 0;
}

与运算过程:


 最后a&b的结果为:

2.|-----按位或

或运算的规则: 2个操作数补码中的2进制位一 一对应,有1则为1,全部为0则为0

例如:

int main()
{
	int a = -10;
	int b = 8;
	int c = a | b;
	printf("%d", c);
	return 0;
}

或运算过程: 

最后a | b的结果为: 

3. ^---按位异或

异或运算的规则: 2个操作数补码中的2进制位一 一对应,相同则为0,不同则为1

例如:

int main()
{
	int a = 10;
	int b = 5;
	int c = a ^ b;
	printf("%d", c);
	return 0;
}

异或运算过程:

最后a ^ b的结果为: 

按位异或的特点:

1.任何数与0异或都等于那个数本身 

例如:

int main()
{
	int a = 10;
	int b = 0;
	int c = a ^ b;
	printf("%d", c);
	return 0;
}

异或过程:

最后的值为:

 

2.任何数与本身异或的值为0

例如:

int main()
{
	int a = 10;
	int b = 10;
	int c = a ^ b;
	printf("%d", c);
	return 0;
}

异或过程:

 

运行结果:

4. ~ ---按位取反 

取反运算的规则: 操作数中的补码,1变成0,0变成1

例如:

int main()
{
	int a = 10;
	printf("%d", ~a);
	return 0;
}

取反过程: 

运算结果:

六. 逗号表达式

逗号表达式,就是⽤逗号隔开的多个表达式。

逗号表达式,从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。

例如:

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

 运行结果:

注意:逗号表达式不能只看最后一个表达式,前面的表达式可能会影响到后面表达式的值 

逗号表达式可以减少一些不必要的代码:

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.1 下标引用操作符[ ]

在我们访问数组中的元素时,就会用到下标引用操作符

操作数:⼀个数组名 + ⼀个索引值(下标)

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("arr[5]=%d\n", arr[5]);
	printf("arr[8]=%d\n", arr[8]);
	return 0;
}

 [ ]的两个操作数是数组名下标

在访问数组中元素时,要确保下标是在有效范围内,不然会造成访问越界的现象:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("arr[10]=%d\n", arr[10]);
	return 0;
}

7.2  函数调⽤操作符( )

当我们调用函数的时候,就会用到函数调⽤操作符

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

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = Add(10, 20);
	printf("%d", a);
	return 0;
}

调用Add函数和printf 库函数 ()就是函数调用操作符,函数调用操作符的操作数是不确定的,但是最少有一个操作数,就是函数名。

八. 结构成员访问操作符

结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个操作数。

例如:

struct Stu //类型声明
{
 char name[15];//名字
 int age; //年龄
};
int main()
{
	struct Stu s1 = { "zhangsan", 20 };
	struct Stu s2 = { .age = 20, .name = "lisi" };
	printf("%s %d\n", s1.name, s1.age);
	printf("%s %d\n", s2.name, s2.age);
	return 0;
}

九. 操作符的属性:优先级、结合性 

C语⾔的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。

1. 优先级

优先级指的是: 如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。 

3 + 4 * 5;

表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * ),由于乘法 的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。

2 结合性

如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = ) 

5 * 6 / 2;

上⾯⽰例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 , 再计算 / 2 。

运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列)

• 圆括号( () )

• ⾃增运算符( ++ ),⾃减运算符( -- )

• 单⽬运算符( + 和 - )

• 乘法( * ),除法( / )

• 加法( + ),减法( - )

• 关系运算符( < 、 > 等)

• 赋值运算符( = )

其他操作符在使⽤的时候可以查看下⾯表格

https://zh.cppreference.com/w/c/language/operator_precedenceicon-default.png?t=N7T8https://zh.cppreference.com/w/c/language/operator_precedence 

以上就是本期操作符的全部内容,如有错误欢迎在评论区指出~,后续会给大家更新一些有关位操作符的题目,让大家更深入体会 位操作符 的用法。 

  

  


 

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值