目录
操作符的分类
算术操作符:+、-、*、/、%
移位操作符:<< >>
位操作符:& | ^ ~
赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
单目操作符:++、--、+、-、&、*、~、(类型)、sizeof、!
关系操作符:>、>=、<、<=、==、!=
逻辑操作符:&&、||、!
条件操作符:?:
逗号表达式:,
下标引用:[ ]
函数调用:( )
结构体成员访问:. ->
1 算术操作符
算数操作符:写代码时涉及计算的一系列操作符。
1.1 + 和 -
加法:+ 减法:-
#include<stdio.h>
int main()
{
int x = 3 + 5;
int y = 6 - 2;
printf("%d\n",x);
printf("%d\n",y);
return 0;
}
+和-是有两个操作数的,上述代码中+的操作数3、5,-的操作数6、2,这种有两个操作数的操作符也称为双目操作符。同理有单目操作符。
1.2 *
操作符*用来完成乘法。
#include<stdio.h>
int main()
{
int num = 3;
printf("%d\n",num*num);//输出9
return 0;
}
1.3 /
运算符/用来完成除法。
#include<stdio.h>
int main()
{
float x = 6/4;
int y = 6/4;
printf("%f\n",x);//输出1.000000
printf("%d\n",y);//输出1
float z = 6.0/4;
printf("%f\n",z);//输出1.500000
return 0;
}
除号两端如果是整数,执行的是整数除法,得到的结果也是整数。
如上示例中x的类型是float(浮点数),但是6/4的结果是1.0,而不是1.5。原因就是除号两端都是整数,执行了整数除法,只返回整数部分,丢弃小数部分。如果想要得到浮点数的结果,两个运算数中必须至少有一个浮点数,如示例中z,6.0/4执行浮点数除法,可得出1.5。
1.4 %
运算符%表示求模(余)运算,即返回两个整数相除的余值。%只能用于整数,不能用于浮点数。
#include<stdio.h>
int main()
{
int x = 6%4;
printf("%d\n",x);//输出2
printf("%d\n",12%-5);//输出2
printf("%d\n",-12%5);//输出-2
printf("%d\n",-12%-5);//输出-2
return 0;
}
由上述示例最后三个可以看出,负数求模规则:结果的正负号由第一个运算数的正负号决定。
2 移位操作符
<<左移操作符
>>右移操作符
移位操作符移的是存储在内存中的二进制位(不能是八进制等),且操作数只能是整数。
2.1 <<左移操作符
移位规则:左边抛弃、右边补0
#include<stdio.h>
int main()
{
int num = 10;
int n = num<<1;
printf("n = %d\n",n);//n=20
printf("num = %d\n",num);//num=10
return 0;
}
num=10,10的2进制是1010,int占4个字节,每个字节8个二进制位,且数值在内存中以补码形式储存,正数补码和原码相同,所以num在内存中的二进制是:
00000000000000000000000000001010 num的2进制表示
00000000000000000000000000010100 num<<1的结果
左边丢掉一位,右边补0。但num本身值不变,num<<1即num向左移一位,将其结果赋值给n,可得出移动后的值。
2.2 >>右移操作符
移位规则(两种):
1.逻辑右移:左边用0补充,右边丢弃
2.算术右移:左边用原来值的符号位补充,右边丢弃
这两种规则的运用取决于编译器,常用算数右移。
#include<stdio.h>
int main()
{
int num = 10;
int n = num>>1;
printf("n = %d\n",n);//n=5
printf("num = %d\n",num);//num=10
return 0;
}
由上述示例可以看出,左移一位有乘二效果,右移一位有除2效果。
移位操作符不能移动负数位,这个标准未定义。
num>>-1错误
3 位操作符
位操作符(操作数必须是整数):
1 & 按位与 规则:有0就为0,全为1才为1
2 | 按位或 规则:有1就为1,全0才为0
3 ^ 按位异或 规则:相同为0,相异为1
4 ~ 按位取反 规则:原0变1,原1变0 (& | ^ 也是双目操作符)
#include<stdio.h>
int main()
{
int num1 = 6;
int num2 = -7;
printf("%d\n",num1&num2);//0
printf("%d\n",num1|num2);//-1
printf("%d\n",num1^num2);//-1
printf("%d\n",~0);//-1
return 0;
}
例1:不能创建临时变量(第三个变量),实现两个整数的交换。
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int temp = 0;//一般交换两个数据,创建temp
temp = a;
a = b;
b = temp;
printf("a=%d b=%d\n",a,b);//a=20 b=10
//0^3 = 3 3^3= 0 因此a^a=0 0^a=a 且满足交换律
//000 -> 011 011 -> 000
//011 011
int x = 5;//不创建临时变量
int y = 8;
x = x^y;//x=5^8
y = x^y;//y=(x^y)^y=x^y^y=x^0=x=5
x = x^y;//x=(x^y)^x=x^y^x=0^y=y=8
printf("x=%d y=%d\n",x,y);//x=8 y=5
return 0;
}
4 赋值操作符
在变量创建时,给一个初始值称为初始化,在变量创建好后,再给一个值,称为赋值。
1 int a = 10;//初始化
2 a = 200;//赋值,这里=是赋值操作符
4.1 连续赋值
1 int a = 3;
2 int b = 4;
3 int c = 0;
4 c=b=a+3;//连续赋值,从右向左依次赋值 c=6
4.2 复合赋值符
1 int a = 10;
2 a+=3;//a=a+3
3 a-=3;//a=a-3
这样的复合赋值符有很多,方便了我们编写代码。
5 单目操作符
单目操作符:只有一个操作数。
5.1 ++和--
++是自增的操作符,--是自减的操作符,都分为前置++/--和后置++/--
5.1.1 前置++
1 int a = 10;
2 int b = ++a;//++的操作数是a,放在a前面,为前置++;a先++,再赋值给b
3 printf("a=%d b=%d\n",a,b);//a=11 b=11 a自身也要++,++后再赋给b
5.1.2 后置++
1 int a = 10;
2 int b = a++;
printf("a=%d b=%d\n",a,b);//a=11 b=10 a自身++,++前赋值给b
5.1.3 前置--
1 int a = 10;
2 int b = --a;
3 printf("a=%d b=%d\n",a,b);//a=9 b=9
5.1.4 后置--
1 int a = 10;
2 int b = a--;
3 printf("a=%d b=%d\n",a,b);//a=9 b=10
5.2 +和-
这里+表示正号,-表示负号,是单目操作符。
1 int a = 10;//即a= +10; 正号一般完全可以省略
2 int b = -a;//b= -10
5.3 & * ~
操作符&为取地址操作符,此时为单目操作符。 &在位操作符中是表示按位与,为双目操作符。
1 int a = 10;
2 printf("%p\n",&a);//输出0x006FFD70
假设a在内存中占的四个字节地址为0x006FFD70、0x006FFD71、0x006FFD72、0x006FFD73,&a后输出0x006FFD70,取出的是a所占四个字节中较小字节的地址。
操作符*为解引用操作符。
1 int a = 10;
2 int* pa=&a;
3 *pa=0;//*pa就是a
*和&都在指针中使用。
~在位操作符中有解释。
5.4 (类型)
(类型) 表示强制类型转换。
1 int a = 3.14;
//a是int类型,3.14是double类型,两边类型不同,编译器会报警告
2 int a = (int) 3.14;
//即将3.14强制转化成int类型,转化后只取整数,这样强制转换可以消除警告。
5.5 sizeof !
sizeof是关键字,也是操作符,用来计算sizeof的操作符数的类型长度,单位是字节。
1 int a = 10;
2 printf("%zd\n",sizeof(int));输出4
!是逻辑操作符,也是单目操作符。
6 关系操作符
用于比较的表达式中运用的操作符为关系操作符。
> 大于 >=大于等于
< 小于 <=小于等于
==相等 !=不相等
多个关系运算符不宜连用 。如:18<=age<=30应改写成age>=18 && age<=30。
7 逻辑操作符
7.1 逻辑取反运算符 !
a | !a |
非0 | 0 |
0 | 1 |
一个变量叫flag,如果flag为真,!flag就是假;如果flag为假,!flag就是真。
7.2 逻辑与运算符 &&
&&是与运算符,也表示并且,如:age>=18&&age<=30。
a | b | a&&b |
非0 | 非0 | 1 |
非0 | 0 | 0 |
0 | 非0 | 0 |
0 | 0 | 0 |
a&&b,两边表达式同真才为真,只要有一个是假的,则整个表达式为假。
7.3 逻辑或运算符 ||
a | b | a||b |
非0 | 非0 | 1 |
非0 | 0 | 1 |
0 | 非0 | 1 |
0 | 0 | 0 |
a||b,两边表达式只要有一个是真,那么整个表达式就是真,两边都为假的时候才为假。
7.4 短路
逻辑运算符特点:先对左侧的表达式求值,再对右侧表达式求值。
若左侧满足条件,就不再对右侧表达式求值。这种情况称为短路。
1 if(month>=3&&month<=5)//若month不满足>=3的条件,左侧为0,右侧表达式无需执行,整个表达式结果为0.
2 if(month==12||month==7)//若month满足左侧,右侧无需执行,整个表达式结果为1.
短路求值:仅根据左侧操作结果就能知道整个表达式的结果,不再对右操作数进行计算的运算。
#include<stdio.h>
int main()
{
int i = 0,a1 = 0,b1 = 2,c1 = 3,d1 = 4;
i = a1++&&++b1&&d1++;//a1=0,右侧无需计算,无需执行,b1c1d1的值不改变 i=0
printf("i=%d a1=%d b1=%d c1=%d d1=%d\n",i,a1,b1,c1,d1);//0 1 2 3 4
int m = 0,a2 = 1,b2 = 2,c2 = 3,d2 = 4;
m = a2++&&++b2&&d2++;//a2=1,b2=3,d2=4,m=1 都执行了
printf("m=%d a2=%d b2=%d c2=%d d2=%d\n",m,a2,b2,c2,d2);//1 2 3 3 5
int n = 0,a3 = 0,b3 = 2,c3 = 3,d3 = 4;
n = a3++||++b3||d3++;//a3=0,b3=3,d3无需计算 n=1
printf("n=%d a3=%d b3=%d c3=%d d3=%d\n",n,a3,b3,c3,d3);//1 1 3 3 4 d3不变
int p = 0,a4 = 1,b4 = 2,c4 = 3,d4 = 4;
p = a4++||++b4||d4++;//a4=1其他无需计算执行,p=1
printf("p=%d a4=%d b4=%d c4=%d d4=%d\n",p,a4,b4,c4,d4);//1 2 2 3 4
return 0;
}
8 条件操作符
条件操作符也叫三目操作符。
1 exp1?exp2:exp3
如果exp1为真,exp2计算,其结果为表达式结果;如果exp1为假,exp3计算,其结果为表达式结果。
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d",&a,&b);//输入 3 2
int m = a>b?a:b;
printf("%d\n",m);//输出3
return 0;
}
9 逗号表达式
逗号表达式,用逗号隔开的多个表达式,从左向右依次执行,最后一个表达式的结果是整个表达式的结果。
1 int a = 1;
2 int b =2;
3 int c=(a>b,a=b+10,b=a+1);//a>b为0,a=2+10=12,b=13 c=13、
4
5 if(a=b+2,c=b/2,d>0)//d>0为判断条件
10 下标引用
操作符:[ ] 双目操作符
操作数:一个数组名+一个索引值(下标)
1 int arr[5];//创建数组
2 arr[3]=4;//实用下标引用操作数
3 [ ]的操作数是arr和3
11 函数调用操作符
操作符:()
一个或多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
Add(3,5)//操作数是Add 3 5
printf("hello\n")
12 结构体成员的访问
12.1 结构体成员的直接访问
结构体变量名.成员名 如:p.x p.y 直接通过.来访问。
#include<stdio.h>
struct Point
{
int x;
int y;
} ;
int main()
{
struct Point p1 = {1,2};
printf("%d %d\n",p1.x,p1.y);//1 2
return 0;
}
12.2 结构体成员的间接访问
结构体变量名->成员名 如:p->x p->y 通过指向结构体的指针访问结构体成员。
#include<stdio.h>
struct Point
{
int x;
int y;
} ;
int main()
{
struct Point p1 = {1,2};
struct Point *ptr = &p1;
printf("%d %d\n",ptr->x,ptr->y);//1 2
return 0;
}