【C语言】操作符(运算符)

目录

1. 算数操作符

2. 移位操作符

2.1 左移操作符

1 << n = 2的n次幂

2.2 右移操作符

n >> 1 和 n / 2    非负数和负偶数时二者等价

3. 位操作符

3.1 按位与

n >> i & 1    获取二进制的i位

n & 1 和 n % 2    非负数时二者等价

n & ~ (1 << i)    将二进制的i位变为0

n & n - 1    将二进制最右边的1变为0

n & -n    提取二进制最右边的1

3.2 按位或

n | 1 << i    将二进制的i位变为1

3.3 按位异或

a ^ a = 0    0 ^ a = a

4. 赋值操作符

5. 单目操作符

6. 关系操作符

7. 逻辑操作符

8. 条件操作符

9. 逗号表达式

10. 下标引用操作符

11. 函数调用操作符

12. 结构成员访问操作符

13. 操作符的属性


1. 算数操作符

+加法,-减法,*乘法,/除法,%取模(取余)

  • %操作符的两个操作数必须为整数
  • +-*/四个操作符可以作用于整数和浮点数
  • /操作符的两个操作数都为整数时,执行整数除法。只要有一个是浮点数,执行浮点数除法

整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法的结果是整数。整数是没有小数部分的数。在C语言中,整数除法结果的小数部分被丢弃,这一过程被称为截断(fruncation)

#include <stdio.h>

int main()
{
	printf("%d\n",   7   /  2);   // 3
	printf("%lf\n",  7.  /  2);   // 3.500000
	printf("%lf\n",  7   /  2.0); // 3.500000
	printf("%lf\n",  7.0 /  2.0); // 3.500000
	printf("%d\n",  -7   /  2);   //-3
	printf("%lf\n",  7   / -2.);  //-3.500000
	printf("%lf\n",  7.0 / -2);   //-3.500000
	printf("%lf\n", -7.  / -2.);  // 3.500000
	return 0;
}

2. 移位操作符

2.1 左移操作符

整数的二进制表示有3种:原码、反码、补码。正整数的原码、反码、补码相同。负整数的反码是原码符号位不变,其他位按位取反,补码是反码+1。整数在内存中存储的是补码。

+7:
原码:00000000000000000000000000000111
反码:00000000000000000000000000000111
补码:00000000000000000000000000000111
-7:
原码:10000000000000000000000000000111
反码:11111111111111111111111111111000
补码:11111111111111111111111111111001

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

int a = 7;
int b = a << 1;

0[00000000000000000000000000001110]

int a = -7;
int b = a << 1;

1[11111111111111111111111111110010]

1 << n = 2的n次幂

int a = 1;
int b = a << 1;

0[00000000000000000000000000000010] = 2的1次幂

int b = a << 2;

00[00000000000000000000000000000100] = 2的2次幂

int b = a << 3;

000[00000000000000000000000000001000] = 2的3次幂

2.2 右移操作符

移位规则:

  • 逻辑移位:左边补0,右边抛弃
  • 算术移位:左边补原符号位,右边抛弃(常见编译器都是算术移位)

算术移位:

int a = 7;
int b = a >> 1;

[00000000000000000000000000000011]1

int a = -7;
int b = a >> 1;

[11111111111111111111111111111100]1

n >> 1 和 n / 2    非负数和负偶数时二者等价

n为非负数时,n >> 1 等价于 n / 2。

10 >> 1 = 5        10 / 2 = 5        0 >> 1 = 0        0 / 2 = 0

n为负偶数时,n >> 1 等价于 n / 2。

-10 >> 1 = -5        -10 / 2 = -5

n为负奇数时,n >> 1 = n / 2 - 1(n >> 1是n除以2向下取整,n / 2直接丢弃小数部分)。

-5 >> 1 = -3        -5 / 2 = -2

3. 位操作符

&按位与,|按位或,^按位异或

位操作符通过逐位比较两个运算对象,生成一个新值。对于每个位:

  • &:两个操作数相应的位都为1,结果为1(有0则0,记忆法:&看起来像0)
  • | :两个操作数相应的位至少有一个为1,结果为1(有1则1,记忆法:|看起来像1)
  • ^:两个操作数相应的位相同为0,相异为1(无进位相加

3.1 按位与

int a = 3;
int b = -5;
int c = a & b;
// a的补码:00000000000000000000000000000011
// b的补码:11111111111111111111111111111011
// c的补码:00000000000000000000000000000011

n >> i & 1    获取二进制的i位

一个数n,假设它的二进制的倒数第一位称为0位,倒数第二位称为1位……想要获取i位,就要在i位上& 1。

以75(000000000000000000000001001011)为例,

要想获取0位,就要在0位上& 1:

 000000000000000000000001001011
&000000000000000000000000000001
=000000000000000000000000000001
=1

要想获取1位,就要在1位上& 1,如果直接& 10(二进制),得到的结果是10(二进制),这个结果并不是我们想要的,我们想要的结果是1。所以把要把0位去掉,再& 1。

先>> 1,再& 1:

75 >> 1 = [000000000000000000000000100101]1

 000000000000000000000000100101
&000000000000000000000000000001
=000000000000000000000000000001
=1

同理,要想获取2位,就要先>> 2,再& 1:

75 >> 2 = [000000000000000000000000010010]11

 000000000000000000000000010010
&000000000000000000000000000001
=000000000000000000000000000000
=0

n & 1 和 n % 2    非负数时二者等价

显然,n & 1表示获取n的二进制的最后一位,奇数的二进制的最后一位一定是1,偶数的二进制的最后一位一定是0。所以,当n为非负数时,n & 1和n % 2等价;当n为负数时,由于取模运算保留符号,n % 2 = -1,而n & 1 = 1。

n为非负数时,n & 1等价于n % 2

  • n为奇数:n & 1 = 1  n % 2 = 1
  • n为偶数:n & 1 = 0  n % 2 = 0

n为负数时,

  • n为奇数:n & 1 = 1  n % 2 = -1
  • n为偶数:n & 1 = 0  n % 2 = 0

n & ~ (1 << i)    将二进制的i位变为0

一个数n,假设它的二进制的倒数第一位称为0位,倒数第二位称为1位……想要将i位修改成0,就要在i位上& 0,其他位都& 1。

以75(000000000000000000000001001011)为例,

要将3位修改成0,就要在3位上& 0,其他位都& 1:

 000000000000000000000001001011
&111111111111111111111111110111
=000000000000000000000001000011

n & n - 1   将二进制最右边的1变为0

n - 1表示将n的二进制最右边的1的右侧区域(包括1)全部按位取反。

n & n - 1表示将n的二进制最右边的1变为0,其余位不变。

24:       00000000000000000000000000011000

23:       00000000000000000000000000010111

24&23:00000000000000000000000000010000

如果n & n - 1为0,则n是2的幂。

+2:  00000000000000000000000000000010

+1:  00000000000000000000000000000001

+4:  00000000000000000000000000000100

+3:  00000000000000000000000000000011

2&1:00000000000000000000000000000000

4&3:00000000000000000000000000000000

n & -n    提取二进制最右边的1

-n表示将n的二进制最右边的1的左侧区域(不包括1)全部按位取反。

+20:       00000000000000000000000000010100

-20:        11111111111111111111111111101100

20 & -20:00000000000000000000000000000100

3.2 按位或

int a = 3;
int b = -5;
int c = a | b;
// a的补码:00000000000000000000000000000011
// b的补码:11111111111111111111111111111011
// c的补码:11111111111111111111111111111011

n | 1 << i    将二进制的i位变为1

一个数n,假设它的二进制的倒数第一位称为0位,倒数第二位称为1位……想要将i位修改成1,就要在i位上| 1,其他位都| 0。

以75(000000000000000000000001001011)为例,

要将2位修改成1,就要在2位上| 1,其他位都| 0:

 000000000000000000000001001011
|000000000000000000000000000100
=000000000000000000000001001111

3.3 按位异或

int a = 3;
int b = -5;
int c = a ^ b;
// a的补码:00000000000000000000000000000011
// b的补码:11111111111111111111111111111011
// c的补码:11111111111111111111111111111000

异或运算支持交换律和结合律,即:

a ^ b = b ^ a        (a ^ b) ^ c = a ^ (b ^ c)

a ^ a = 0    0 ^ a = a

a ^ a = 0:

如,3 ^ 3 = 00000011 ^ 00000011 = 00000000

0 ^ a = a:

如,0 ^ 3 = 00000000 ^ 00000011 = 00000011

不创建临时变量实现两个数的交换:

#include <stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b; // 10^20
	b = a ^ b; // 10^20^20=10^0=10
	a = a ^ b; // 10^20^10=0^20=20
	printf("a = %d b = %d\n", a, b);
	return 0;
}

4. 赋值操作符

=赋值

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

x += 10; // 等价于x = x + 10;

C语言支持多重赋值,其他很多语言都不支持。赋值的顺序是从右往左。

a = b = c = d = 10;

5. 单目操作符

单目就是一元的意思,只有一个运算对象。

!逻辑反操作

-负值,+正值

&取地址,*间接访问操作符(解引用操作符)

sizeof计算变量或类型所创建的变量的长度(单位:字节),返回size_t类型的值

~对一个数的二进制按位取反

++自增,--自减(前置++:先++,后使用,后置++:先使用,后++)

(类型)强制类型转换

6. 关系操作符

>大于,>=大于或等于,<小于,<=小于或等于,!=不相等,==相等

7. 逻辑操作符

&&逻辑与:两个表达式全为真,整个表达式为真

||逻辑或:两个表达式有一个为真,整个表达式为真

短路原则:

  • exp1&&exp2:

当exp1为真时,再判断exp2的真假,来确定整个表达式的真假;

当exp1为假时,可以确定整个表达式为假,就不用判断exp2的真假(即不执行exp2)。

  • exp1||exp2:

当exp1为真时,可以确定整个表达式为真,就不用判断exp2的真假(即不执行exp2);

当exp1为假时,再判断exp2的真假,来确定整个表达式的真假。

#include <stdio.h>

int main()
{
	int a = 1;
	int b = 2;
	if (a == 10 && b-- == 2) // a == 10为假,可以确定整个表达式为假,不执行b-- == 2
		printf("a=%d b=%d\n", a, b);
	else
		printf("a=%d b=%d\n", a, b); // a=1 b=2

	int c = 3;
	int d = 4;
	if (c < 10 || d++ == 4) // c < 10为真,可以确定整个表达式为真,不执行d++ == 4
		printf("c=%d d=%d\n", c, d); // c=3 d=4
	else
		printf("c=%d d=%d\n", c, d);

	return 0;
}

8. 条件操作符

exp1?exp2:exp3

条件操作符是唯一的三目操作符。先求解exp1,若其值为真,则将exp2的值作为整个表达式的取值,否则将exp3的值作为整个表达式的取值。

9. 逗号表达式

exp1,exp2,exp3,...,expN

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

10. 下标引用操作符

[]下标引用操作符

p[i]=*(p+i)     p[i][j]=*(p[i]+j)=*(*(p+i)+j)

11. 函数调用操作符

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

如,test(),Add(a,b)。

12. 结构成员访问操作符

  • 结构体变量.成员变量
  • 结构体指针->成员变量
#include <stdio.h>
 
struct Book
{
	char bookName[20];
	char authorName[20];
	int publishYear;
};
 
struct Novel
{
	struct Book b;
	char type[10];
	char length[10];
};
 
int main()
{
	struct Novel n = { { "射雕英雄传","金庸",1957 },"武侠","长篇" };
	struct Novel* p = &n;
	printf("%s %s %d %s %s\n",    n.b.bookName,    n.b.authorName,    n.b.publishYear,    n.type,    n.length);
	printf("%s %s %d %s %s\n", (*p).b.bookName, (*p).b.authorName, (*p).b.publishYear, (*p).type, (*p).length);
	printf("%s %s %d %s %s\n",   p->b.bookName,   p->b.authorName,   p->b.publishYear,   p->type,   p->length);
	return 0;
}

13. 操作符的属性

表达式(expression)由操作符和操作对象组成,每个表达式都有一个值,必须根据操作符的执行顺序来求值。

操作符的执行顺序取决于它们的优先级,如果优先级相同,取决于他们的结合性。

C语言操作符优先级有15级,按优先级从大到小排序:

优先级

操作符

描述

用法示例

结合性

1

()

聚组/函数调用

(表达式)/函数名(形参声明)

L-R

[]

下标引用

数组名[常量表达式]

.

访问结构成员结构成员访问结构体.成员名

->

访问结构指针成员结构体指针->成员名

2

+

正值

单目操作符

+表达式

R-L

-

负值

-表达式

(类型)

强制类型转换

(数据类型)表达式

++

前置自增

++变量

++

后置自增

变量++

--

前置自减

--变量

--

后置自减

变量--

*

解引用

*指针变量

&

取地址

&变量

!

逻辑非

!表达式

~

按位取反

~表达式

sizeof

计算长度

sizeof(数据类型/变量)

3

*

乘法

算术操作符

表达式*表达式

L-R
 

/

除法

表达式/表达式

%

取模

整型表达式/整型表达式

4

+

加法

表达式+表达式

-

减法

表达式-表达式

5

<<

左移

移位操作符

变量<<表达式

>>

右移

变量>>表达式

6

>

大于

关系操作符

表达式>表达式

>=

大于或等于

表达式>=表达式

<

小于

表达式<表达式

<=

小于或等于

表达式<=表达式

7

==

等于

表达式==表达式

!=

不等于

表达式!=表达式

8

&

按位与

位操作符

表达式&表达式

9

|

按位或

表达式|表达式

10

^

按位异或

表达式^表达式

11

&&

逻辑与

逻辑操作符

表达式&&表达式

12

||

逻辑或

表达式||表达式

13

?:

条件操作符

表达式1?表达式2:表达式3

R-L

14

=

赋值

赋值操作符

变量=表达式

+=

加后赋值

变量+=表达式

-=

减后赋值

变量-=表达式

*=

乘后赋值

变量*=表达式

/=

除后赋值

变量/=表达式

%=

取模后赋值

变量%=表达式

<<=

左移后赋值

变量<<=表达式

>>=

右移后赋值

变量>>=表达式

&=

按位与后赋值

变量&=表达式

|=

按位或后赋值

变量|=表达式

^=

按位异或后赋值

变量^=表达式

15

,

逗号

表达式,表达式,…

L-R

1. 优先级

#include <stdio.h>

int main()
{
    // 优先级:自增>加法>赋值>逗号	
    int a, b, c;
	a = 5;
	c = ++a;//a=6,c=6
	b = ++c, c++, ++a, a++; // c=7,b=7,c=8,a=7,a=8
	b += a++ + c; // b=7+8+8=23,a=9
	printf("a = %d b = %d c = %d\n:", a, b, c); // a = 9 b = 23 c = 8
	return 0;
}

2. 结合性

*p++:解引用(*)和后置自增(++)优先级相同,结合性都是从右往左,所以*p++等价于*(p++):

  1. 先执行*p
  2. 再p++
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值