1,算术操作符+- * / %
其中+ - * /适用于整数或浮点数,%必须是两个整数,不然会报错的。
![](https://i-blog.csdnimg.cn/blog_migrate/700c9bb598c2705516c8aff887e47c82.png)
小数除法只有表达式中至少有一个浮点数才行
2,移位操作符>> <<
操作数只能是整形的二进制,移动补码,移“位”就是移“二进制位”
负数的第一个符号位0正 1负,三者转换关系如下图。正数的原反补相同,没有转换关系
![](https://i-blog.csdnimg.cn/blog_migrate/ec4f3db598e9fdc09b09886c33adcd34.png)
负数的原码反码补码
2.1左移操作符
![](https://i-blog.csdnimg.cn/blog_migrate/c43ef14d271169c77a6be04913da6de3.png)
正数左移
![](https://i-blog.csdnimg.cn/blog_migrate/1415e27c246344c64fbcf3171a47153e.png)
负数左移
我们发现,左移一位就是原来数字的二倍,左移两位就是原来数字的四倍
a=10 , a<<1=20 , a<<2=40
2.2右移操作符,
算术右移还是逻辑右移取决于编译器,大多数编译器是算术右移。
算数右移:右边丢掉,左边补原来的符号位(0正 1负)
逻辑右移:右边丢掉,左边补0(正数的符号位)
-1向右移一位后还是-1,-1的补码存储在内存中,全1内存中存储是二进制的补码,为了方便展示用十六进制全1就是ff ff ff ff,1个十六进制位是四个二进制位。
![](https://i-blog.csdnimg.cn/blog_migrate/6b902f56e0f5606fb4a0fb6953c10bd1.png)
-1是以补码的形式存储的
![](https://i-blog.csdnimg.cn/blog_migrate/ebc755bfee079b969e2f5005f6b5a405.png)
-1的右移一位,打印是原码,移动二进制位=移动内存中的补码
![](https://i-blog.csdnimg.cn/blog_migrate/99e8e8004001253c60934f483f30a3d6.png)
-1向右移一位后还是-1
3,位操作符 & | ^
它们的操作数必须是整数,内存中存储的补码,二进制位计算,按“位”与,就是按”二进制位“与。
3的原反补0000…0011
-5的原码1000…0101 反码1111…1010 补码1111…1011
按位与&,有0为0,同1为1。3&-5 结果是0000…0011
![](https://i-blog.csdnimg.cn/blog_migrate/74f58e1dc1fbfcc1074a6798ce5f00aa.png)
按位或 | ,有1为1,同0为0。3|-5结果是1111…1011。因为是负数所以转化为原码是1000…0100,1000 …0101 原码来打印。
![](https://i-blog.csdnimg.cn/blog_migrate/6dc30a5c7eb79a57f6066585e90d66ff.png)
按位异或^,相同为0,相异为1。3^-5结果是1111…1000。因为是负数所以转化为原码是1000…0111,1000…1000原码来打印。
![](https://i-blog.csdnimg.cn/blog_migrate/437721b397c86a9e62c126e9d28f367a.png)
按位异或操作符的用法:(缺点是不易阅读)
//不允许创建临时变量tmp,交换两个整数的内容
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("%d %d", a, b);
return 0;
}
4,赋值操作符 += -= *= &= |= ^= >>=
5,单目操作符
5.1逻辑反操作 !
非0真—!—>0是假
0是假—!—>1是真
![](https://i-blog.csdnimg.cn/blog_migrate/e0721566c7011debc88ed33d89ad03e5.png)
C99引入布尔类型_Bool,用来表示真假的类型来创建临时变量flag,只有两个取值true,false,本质是0,1,需要引用头文件<stdbool.h>,也可以当作函数的返回类型,函数返回true/false
![](https://i-blog.csdnimg.cn/blog_migrate/bfaae773716a17fc836e6ab71b7adc23.png)
5.2负值- 正值+
unsigned int 类型角度来看-10的补码直接输出,没有符号位,没有正负之分,无原反补概念,内存中就是他的值
5.3取地址& 解引用*
5.4sizeof
sizeof是关键字,自己定义的名字不可以用关键字sizeof,语言已经用了
sizeof是操作符,功能上是用来计算变量类型,数组类型的大小,操作符计算可以省略括号sizeof arr可以,sizeof int有时候可以。
sizeof不是函数,函数调用后面的括号一定不可以省略
sizeof内部表达式不参与计算,sizeof是用类型来计算的
![](https://i-blog.csdnimg.cn/blog_migrate/5b9cd4beffa659744f33d3fd7e8c0724.png)
5.5按位取反~
对一个数的二进制位按位取反,包括符号位,我们先把1左移三位得到0000…1000再按位取反即可得到1111…0111 。知道了这个我们就可以不改变其他位,只改变二进制的某一位了。
可不可以把0000…1001某一位改成0?按位与1111…0111,得到0000…0001
可不可以把0000…0001某一位改成1?按位或0000…1000,得到0000…1001
![](https://i-blog.csdnimg.cn/blog_migrate/bd0bc9b0695c8373818973944988edbf.png)
5.6前置后置++--
![](https://i-blog.csdnimg.cn/blog_migrate/42c0288daa708745748d741f6e7656b1.png)
先自加,再使用
![](https://i-blog.csdnimg.cn/blog_migrate/32e4140f019f2148a578bc3aeea8a219.png)
先使用,再自加
5.7强制类型转化
5.8sizeof和数组
6,关系操作符> >= != == < <=
7,逻辑操作符&& ||
只关注真假,是非0还是0,是真还是假,3是真,5是真,10是真,0是假
逻辑与操作符&&左边为假,右边不计算
逻辑或操作符 | | 左边为真,右边不计算
![](https://i-blog.csdnimg.cn/blog_migrate/0301a6fa9cac39fe1d32c943f392e9b1.png)
8,条件操作符
(exp1)?exp2:exp3
exp1是真,计算exp2作为整个表达式的结果,不计算exp3
exp1是假,不计算exp2,计算exp3作为整个表达式的结果
9,逗号表达式
exp1,exp2,exp3,...expN
从左到右执行,整个表达式的结果是最后一个表达式的结果,执行过程中可能会影响最后一个表达式的
结果。
![](https://i-blog.csdnimg.cn/blog_migrate/9cd3ef6fd21262c3b5882012c3488cab.png)
从左到右依次执行
![](https://i-blog.csdnimg.cn/blog_migrate/789f155680f551092787a9cebca55e5f.png)
逗号表达式的使用
10,下标引用,函数调用和结构成员
10.1下标引用操作符[]
两个操作数是"数组名"和"下标"缺一不可printf("%d\n",arr[4]);
10.2函数调用操作符()
两个操作数是"函数名"和"参数",至少有一个操作数,因为有时候函数没有参数,调用函数时括号不可以省略。
10.3访问一个结构的成员
内置类型:char,int,short,long,float,double。
自定义类型(=聚合类型):结构体类型,枚举类型,联合体类型。把简单的的类型(有字符串,数组…)多个聚合在一起,形成了复杂的描述形式
结构体.成员名
结构体指针->成员名
//创建好了一个类型,struct Book就是一个类型
struct Book
{
char name[20];
int price;
char author[20];
}b1;
//创建了一个全局变量b1
void Print(struct Book* b2)
{
printf("%s\n", (*b2).author);//可以
printf("%s\n", b2->author);//传址调用,操作符访问结构体内部
}
int main()
{
struct Book b2 = { "the book",20,"rose" };// 创建局部变量b2初始化
printf("%s,%d", b2.name, b2.price);//用操作符.获取b2里面的信息
Print(&b2);//不能只传b2过去,必须取地址后才是传址调用
return 0;
}