目录

1.算术操作符

2.移位操作符

左移

右移

3. 位操作符

& 按位与

| 按位或

^ 按位异或

~  按位取反

 4.赋值操作符

5.单目操作符

6.关系操作符

7.逻辑操作符

8.条件操作符

9.逗号表达式

10.下标引用、函数调用和结构成员

11.表达式求值

 11.1 隐式类型转换

整形提升

11.2 算术转换

11.3 操作符优先级


操作符在编程运算中有重要作用,本文就来学习一下C语言的操作符。

1.算术操作符

        + - * /

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

2.移位操作符

        <<   >>

在介绍移位操作符前先学习一下二进制的表示方法,二进制表示方法有3种:

原码,反码,补码

正数原码,反码,补码相同

负数原码,反码,补码需要计算

例子:5的二进制码

0000 0000 0000 0000 0000 0000 0000 0101        原码

0000 0000 0000 0000 0000 0000 0000 0101        反码

0000 0000 0000 0000 0000 0000 0000 0101        补码

第一位数表示正负。0为正数,1为负数。

          -5的二进制码

1000 0000 0000 0000 0000 0000 0000 0101        原码

1111  1111  1111  1111 1111  1111  1111  1010        反码 (符号位不变,其他位按位取反

1111  1111  1111  1111 1111  1111  1111  1011        补码(最后一位+1)

整数在内存中存的是二进制的补码。回到移位操作符。

左移

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
	int a = 5;
	a = a<< 1;
	printf("%d", a);
	return 0;
}

上段代码将 5 的补码左移一位,再最后一位补上0

 

 

 此时二进制补码变成了 2³+2=8+2=10 。  

输出结果

10

 这次对-5左移一位

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
	int a = -5;
    //-5 的补码1111  1111  1111  1111 1111  1111  1111  1011
	a = a<< 1;
	printf("%d", a);
	return 0;
}

打印时,将打印原码,因此会先将补码转成原码。正数因为源反补都相同,不需要再换算。

        计算发现 -5左移1位原码就是 - (2³+2)= -10 ,打印结果自然也是-10接下来介绍右移,右移分为算术右移和逻辑右移。

右移

算术右移:右边丢弃,左边补符号位

逻辑右移:右边丢弃,左边补0 。 

+5 -5 的原反补码及 对补码算术右移1位并转为原码

 截图时截少了,左边的0没截到

代码:

	int a = -5;
	a = a >> 1;
	printf("%d", a);

 结果:

-3

右移运算是算术还是逻辑右移,是取决于编译器的,此次结果表明当前编译器采用的是算术右移。

注意:无法移动负数位,标准里未定义。

 a>>>-1 //error

3. 位操作符

        | & ^ ~

        &按位与:类似且的逻辑,二进制按位运算,前后两个运算数同时为“1”得“1”。 

        | 按位或:前后两个运算数只要其一为“1”,结果就为“1”。

        ^按位异或:前后两个运算数相同为0,相异为1。

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

& 按位与

程序

    int a = -3;
	int b = 5;
	int c = a & b;
	printf("%d", c);

同样先算出a,b的补码

    a=-3,b=5
    a 10000000000000000000000000000011 原
	a 11111111111111111111111111111100 反
	a 11111111111111111111111111111101 补
    b 00000000000000000000000000000101 补

按位与 a&b,每位两者都为1,才得1,否则0, 得:

	a 11111111111111111111111111111101 补
    b 00000000000000000000000000000101 补
    c 00000000000000000000000000000101 补

因为打印时会打印原码,这里再将补码转为原码,即:

	因为c为正数所以正反补都相同
    c 00000000000000000000000000000101 补
	c 00000000000000000000000000000101 反
	c 00000000000000000000000000000101 原

运行结果

5

| 位或

同理

程序

    int a = -3;
	int b = 5;
	int c = a | b;
	printf("%d", c);

运行结果

-3

^ 按异或

        相同为0,相异为1。

程序

void test4() {
	int a = -3;
	int b = 5;
	int c = a ^ b;
	printf("%d", c);
}

运行结果

-8

异或特殊的两种用法,任何数与0异或是它本身,任何数与它本身异或是0。

程序

	int a = 3;
	printf("%d\n", a ^ 0);
	printf("%d", a ^ a);

运行结果

3
0

运算过程

                 相同取0 相异取1
 a   000000000000000000000000000000000011 原
 a   111111111111111111111111111111111100 反
 a   111111111111111111111111111111111101 补
 0   000000000000000000000000000000000000 补
a^0  111111111111111111111111111111111101 补

 a   111111111111111111111111111111111101 补
 a   111111111111111111111111111111111101 补
a^a  000000000000000000000000000000000000 补

 可以发现a^0=a,a^a=0 。由此又延伸出一种很常用的算法,异或一组数据就能得到其中不成对的数字。

对1234321这组数据异或

        1^2^3^4^3^2^1=4

成对的数字相抵消为0,最后剩下一个4与0异或,就是4本身。

这里能听懂,那这道题对你来说那必是轻而易举(~ ̄▽ ̄)~力扣

有了这样的基础,你甚至能拿两个变量做更nb的操作——无需第三个变量就能交换两个变量的值

	int a = 3;
	int b = 5;
	a = a ^ b;
	b = a ^ b;//此时a = a ^ b;
	a = a ^ b;//此时b=a^b^b =a,也就是a =a^b^a 最终等于b
	printf("a=%d b=%d", a, b);

运行结果

a=5 b=3

当然这里也可以用求和的方式实现交换,但这种方法有缺陷的,当a ,b的值非常大时,他们的和就会溢出,最终得不到想要的值,算权宜之计。

~  按位取反

        将补码全部取反,打印的时候同样要转换成原码打印。 

1.下面的程序将二进制某位从0变成1又从1变成0,1变成0的运算用到“~”取反,具体过程看注释。

000000000000000000000000000000001011 
                    ↓            ↑目标位
000000000000000000000000000000001111
                    ↓            ↑目标位
000000000000000000000000000000001011 

程序

    //~取反
    //000000000000000000000000000000001011
	int a = 11;
	a |= (1 << 2);//对1左移2位
    //000000000000000000000000000000001011     a
    //000000000000000000000000000000000100   (1 << 2)
    //000000000000000000000000000000001111  进行位或运算得到结果
	printf("变形:%d\n", a);
    
	a &= ~(1 << 2);
    //000000000000000000000000000000001011      a
    //111111111111111111111111111111111011   ~(1 << 2)
    //000000000000000000000000000000001011  进行位与运算得到结果
	printf("还原:%d\n", a);

 运行结果

变形:15
还原:11

2. 可以用来终止获取输入  while(~scanf() )

        scanf 读取失败时返回 -1,而-1的补码是32/64位1,取反后为0,即返回假,所以条件终止。

-1    1111 1111 1111 1111 1111 1111 1111 1111
 0    0000 0000 0000 0000 0000 0000 0000 0000 

 4.赋值操作符

        赋值,字面理解可以给变量赋值的操作符。

复合赋值符

+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果。

5.单目操作符

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

6.关系操作符

关系操作符

>
>=
<
<=
!=  用于测试“不相等”
==    用于测试“相等”

7.逻辑操作符

&&   逻辑与
||      逻辑或

        区分逻辑与按位与,区分逻辑或按位或。

        逻辑与遇到假就停止判断后面的操作数;

        逻辑或遇到真就停止判断后面的操作数。

8.条件操作符

exp1 ? exp2 : exp3

        判断是否满足exp1,真执行exp2,假执行exp3。可以做比大小的函数。

9.逗号表达式

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

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

        c最后将收到最后一个表达式,b的返回值,变成13。        

10.下标引用、函数调用和结构成员

        1. [ ] 下标引用操作符

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

        2. ( ) 函数调用操作符
        接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

        3. 访问一个结构的成员

11.表达式求值

        表达式求值的顺序一部分是由操作符的优先级和结合性决定,操作过程可能需要转成其他类型。

 11.1 隐式类型转换

        精度较小的类型数据与精度大的类型数据运算时,会把精度小的类型转为精度大的类型再运算。C的整型算术运算总是至少以缺省整型类型的精度来进行的。

整形提升

        为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

整形提升的例子:

//实例
int main()
{
    char a = 0xb6;
    short b = 0xb600;
    int c = 0xb6000000;//0xb6,0xb600,0xb6000000这些都是整形常数
    if(a==0xb6)    //a整形提升后前面为一大串1,0xb6前面为一大串0
    printf("a");
    if(b==0xb600)    
    printf("b");
    if(c==0xb6000000)
    printf("c");
    return 0;
}

实例中的a,b要进行整形提升,但是c不需要整形提升。(因为c是整形,)
a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真.
所程序输出的结果是:

c

11.2 算术转换

        当操作符的各个操作数的类型不相同时,按下面的顺序,将一个操作数类型转换成另一个操作数类型再进行运算。

long double
double
float
unsigned long int
long int
unsigned int
int

排名在下的类型将往上转换

如:float 见double 会先将float转换成double再运算。

11.3 操作符优先级

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

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

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

<< 

左移

变量<<表达式

左到右

双目运算符

>> 

右移

变量>>表达式

6

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

小于

表达式<表达式

<=

小于等于

表达式<=表达式

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

--

/=

除后赋值

变量/=表达式

--

*=

乘后赋值

变量*=表达式

--

%=

取模后赋值

变量%=表达式

--

+=

加后赋值

变量+=表达式

--

-=

减后赋值

变量-=表达式

--

<<=

左移后赋值

变量<<=表达式

--

>>=

右移后赋值

变量>>=表达式

--

&=

按位与后赋值

变量&=表达式

--

^=

按位异或后赋值

变量^=表达式

--

|=

按位或后赋值

变量|=表达式

--

15

逗号运算符

表达式,表达式,…

左到右

--

来源