目录
1.操作符分类
2.算数操作符
3.移位操作符
3.1铺垫知识
二进制序列
原码、反码、补码
3.2左移操作符
3.3右移操作符
4.位操作符
4.1按位与
4.2按位或
4.3按位异或
5.赋值操作符
5.1赋值操作符
5.2复合赋值符
6.单目操作符
6.1逻辑反操作符 !
6.2取地址符 &
6.3解引用操作符 *
6.4sizeof 操作符
6.5按位取反操作符 ~
6.6自增自减操作符 -- ++
7.关系操作符
8.逻辑操作符
逻辑与 &&
逻辑或 ||
9.条件操作符
10.逗号表达式
例1
例2
11.下标引用、函数调用和结构成员
11.1下标引用操作符
11.2函数调用操作符
11.3访问结构成员
12.表达式求值
12.1隐式类型转换
12.2算术转换
12.3操作符的属性
操作符优先级和结合性
13.结束
- 算数操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
+ - * / %
我们可以在VS中运算一下
<< 左移操作符
>> 右移操作符
注意:移位操作符的操作数只能是整数
可以这样写代码
int x = 7 >> 1;
移位操作符移动的是二进制位
对于一个整数是4个字节,一个字节是8个bit位,那么一个整数就是32个bit位
一个整数写出二进制序列的时候,就是32个bit位
- 对于有符号整数来说,最高位是符号位:符号位是1表示负数,符号位是0表示正数
- 对于无符号整数来说,所有位都是有效位,没有符号位
所以,负数写成二进制序列的话,最高位一定是1;正数写成二进制序列,最高位一定是0;
整数的二进制表示形式有三种:原码、反码、补码
对于正整数来说,原码、反码、补码相同
对于负数来说
原码:按照数值的正负,直接写出的二进制序列
反码:原码的符号位不变,其他位按位取反(0变成1,1变成0)
补码:反码的二进制+1
0当作无符号数看待
整数在内存中存的都是补码
举个例子:
10:
原码:00000000 00000000 00000000 00001010
反码:00000000 00000000 00000000 00001010
补码:00000000 00000000 00000000 00001010
-10:
原码:10000000 00000000 00000000 00001010
反码: 11111111 11111111 11111111 1111 0101
补码: 11111111 11111111 11111111 1111 0110
我们用图来解释
左移一位即是,二进制序列向左移动一位,在末尾补一个0,形成新的二进制序列
移位的时候移动的是补码
左移n位效果相当于,乘上2的n次方
m只参与运算,m的值不变,这里m移位的结果其实是n的值
所以我们总结下来,向左移位的规则就是:左边丢弃,右边补0
负数同样的道理
移位规则:
首先右移运算分为两种:
- 逻辑移位:左边用0填充,右边丢弃
- 算数移位:左边用原该值的符号位填充,右边丢弃
逻辑右移还是算数右移,取决于编译器 ;绝大部分编译器采取的是算数右移
右移n位的效果相当于,除以n的k次方
注意:不管是左移还是右移,不要移动负数位,比如:>>-1,<<-1
位操作符有:
& //按位与
| //按位或
^ //按位异或
注意:操作数必须是整数
这里我们所说的按位,都是按二进制位
都是按照补码进行运算的
举个例子
所以,按位与 & 的运算规则是
- 只要有0则为0
- 两个都是1才为1
- 结果是补码,需要还原成原码到十进制数
按位与的使用,可以得到想得到的位:先移位,再按位与
比如,我想得到3的最低位,那么我就按位与1
如果想得到第n位,那么可以把第n位移到最低位,再按位与1
举个例子
所以,按位或 | 的运算规则是
- 有1则为1
- 全0才为0
- 结果是补码,需要还原成原码到十进制数
举个例子
异或运算的特点:
- 相同为0,相异为1
- 结果是补码,需要还原成原码到十进制数
按位异或的使用,根据相同为0,相异为1;比如
a^a=0
0^a=a
我们可以利用异或运算的特点,用不创建临时变量的方法交换两个数的值;看代码
int main()
{
int a, b;
scanf("%d %d", &a, &b);
a = a ^ b;
b = a ^ b;//即 a ^ b ^ b = a ^ 0 = a;
a = a ^ b;//即 a ^ b ^ a = 0 ^ b = b;
printf("%d %d", a, b);
return 0;
}
赋值操作符是一个非常棒的操作符,他可以让你得到一个你之前不满意的值,也就是可以重新赋值
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值
赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y + 1;//连续赋值
这样的写法更加清晰而且易于调试
+= -= *= /= %= >>= <<= &= |= ^=
这些运算符都可以写成复合的效果,比如:
int x = 10;
x = x + 10;
x += 10;//复合赋值
同样的道理,这样写更加简洁
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置 --
++ 前置、后置 ++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
想要把假变成真,把真变成假的时候
如果 a 为真,则!a为假
int a = 10;
&a;//a变量的地址
int arr[10];
&arr;//这是数组的地址
int*p = &a;//可以用指针变量接收地址
解引用操作符 * 和取地址符 & 通常是搭配使用的
*p;//对p进行解引用操作,*p是通过p中存放的地址找到p指向的对象
sizeof 是操作符而不是函数
~的作用是全部取反,无论是符号位还是其他位,比如:
++是一种自增1的操作
自增分为:
前置++:即++a,先+1,后使用
后置++:即a++,先使用,后+1
--是一种自减1的操作
自减分为:
前置 --:即--a,先-1,后使用
后置 --:即a--,先使用,后-1
> 大于
>= 大于等于
< 小于
<= 小于等于
!= 不等于 用于测试“不相等”
== 等于(表判断) 用于测试“相等”
在编程的过程中,注意不要把 == 和 = 写错了
- ==表判断
- =表赋值
&& 逻辑与
|| 逻辑或
区分逻辑与和按位与
区分逻辑或和按位或
1&2 ------>0
1&&2 ------>1 //逻辑与
1|2 ------>3
1||2 ------>1 //逻辑或
逻辑与&&表示并且的意思:同时为真才为真
举个例子:a&&b&&d++
//这个时候如果a为假,则&&后面不再执行;如果a为真,则判断b,b如果为假,则&&后面的d++不再执行
逻辑或 || 表示或者的意思:一个为真就为真
举个例子:a||b||d++
//这个时候如果a为假,则判断b,b如果为假,则||后面的d++不再执行;如果a为真,则||后面不再执行
条件操作符也称为三目操作符
exp1 ? exp2 : exp3
条件操作符的作用是:
exp1 , exp2 , exp3 , ... expN
逗号表达式,就是用逗号隔开的多个表达式
逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果
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) {
//业务处理
}
"[ ]"就是下标引用操作符,用在数组中
操作数:一个数组名+一个索引值
int arr[10];//创建数组
arr[9] = 10;//使用下标引用操作符
[ ]的两个操作数是arr和9
[ ]的操作数有两个
"( )"就是函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩下的操作数是传递给函数的参数
void test(int a, int b) {
return a + b;
}
int main() {
int a, b;
test(a, b);
return 0;
}
函数名和参数都是()的操作数
. 结构体.成员名
-> 结构体指针->成员名
结构体.成员名
struct Book {
char name[20];
int price;
};
int main() {
struct Book b = { "C语言指南",55 };
printf("%s %d", b.name, b.price);
return 0;
}
结构体指针->成员名
struct Book {
char name[20];
int price;
};
void Print(struct Book* pb)
{
printf("%s %d", pb->name, pb->price);
}
int main() {
struct Book b = { "C语言指南",55 };
Print(&b);
return 0;
}
这两句代码是等价的
printf("%s %d", (*pb).name, (*pb).price);
printf("%s %d", pb->name, pb->price);
表达式求值的顺序一部分是由操作符的优先级和结合性决定
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
C的整型算数运算总是至少以缺省型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
整型提升的意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换位CPU内整型操作数的标准长度
通用CPU是难以直接实现两个8bit字节直接相加运算,所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算
整型提升是按照变量的数据类型的符号位来提升的
整型提升针对的是自身大小 小于整型大小的操作数
如果某个操作符的各个操作数属于不同的类型,那么除非其中以一个操作数转换为另一个操作数的类型,否则操作就无法进行
下面的层次体系称为寻常算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型再上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算
比如,int 和 float 运算,会把 int 转换成 float(向上转换)
但是算术转换要合理,要不然会有一些潜在的问题
复杂表达式的求值有三个影响因素:
两个相邻的操作符先执行哪一个?取决于他们的优先级,如果两者的优先级相同,取决于他们的结合性
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
----- |
() |
圆括号 |
(表达式)/函数名(形参表) |
----- |
||
. |
成员选择(对象) |
对象.成员名 |
----- |
||
-> |
成员选择(指针) |
对象指针->成员名 |
----- |
||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
(类型) |
强制类型转换 |
(数据类型)表达式 |
----- |
||
++ |
前置自增运算符 |
++变量名 |
单目运算符 |
||
++ |
后置自增运算符 |
变量名++ |
单目运算符 |
||
-- |
前置自减运算符 |
--变量名 |
单目运算符 |
||
-- |
后置自减运算符 |
变量名-- |
单目运算符 [4] |
||
* |
取值运算符 |
*指针变量 |
单目运算符 |
||
& |
取地址运算符 |
&变量名 |
单目运算符 |
||
! |
逻辑非运算符 |
!表达式 |
单目运算符 |
||
~ |
按位取反运算符 |
~表达式 |
单目运算符 |
||
sizeof |
长度运算符 |
sizeof(表达式) |
----- |
||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
双目运算符 |
||
% |
余数(取模) |
整型表达式/整型表达式 |
双目运算符 |
||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
双目运算符 |
||
5 |
<< |
左移 |
变量 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
双目运算符 |
||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
双目运算符 |
||
< |
小于 |
表达式 |
双目运算符 |
||
<= |
小于等于 |
表达式 |
双目运算符 |
||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
双目运算符 |
||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
----- |
/= |
除后赋值 |
变量/=表达式 |
----- |
||
*= |
乘后赋值 |
变量*=表达式 |
----- |
||
%= |
取模后赋值 |
变量%=表达式 |
----- |
||
+= |
加后赋值 |
变量+=表达式 |
----- |
||
-= |
减后赋值 |
变量-=表达式 |
----- |
||
<<= |
左移后赋值 |
变量 |
----- |
||
>>= |
右移后赋值 |
变量>>=表达式 |
----- |
||
&= |
按位与后赋值 |
变量&=表达式 |
----- |
||
^= |
按位异或后赋值 |
变量^=表达式 |
----- |
||
|= |
按位或后赋值 |
变量|=表达式 |
----- |
||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
- 优先级与求值顺序无关。如a+b && b*c,虽然*优先级最高,但这个表达式求值顺序是从左到右
- 优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级
- 相同优先级中,按结合性进行结合。大多数运算符结合性是从左到右,只有三个优先级是从右至左结合的,它们是单目运算符、、条件运算符、赋值运算符
那么今天的学习就到这里咯,今天我们学习了操作符的知识
小杜跟各位小伙伴在一起成长,祝我们都能成为大牛!
//小杜的成长之路