目录
一.操作符的分类:
1.算术操作符2.移位操作符3.位操作符4.赋值操作符5.单目操作符6.关系操作符7.逻辑操作符8.条件操作符9.逗号表达式10.下标引用、函数调用和结构成员
1.算术操作符
+ - * / %
%(取余) 结果是余数。
%操作符的两个操作数必须是整数,则最后返回的数是余数。
其余的操作符可以是整数也可以是浮点数。
对应 / 操作符如果两个都是整数,则执行整数除法。
2.移位操作符
<< 左移操作符>> 右移操作符这个移位是指在二进制的基础上进行的。而整数的内存的存储是补码的二进制数
如:
int a=5;
这个5是十进制数,转化二进制就是101,又因为a是整数,占4个字节,32个bit位,则:
原码:00000000 00000000 00000000 00000101
反码:00000000 00000000 00000000 00000101
补码:00000000 00000000 00000000 00000101
(正整数的原码,反码,补码都是一致的)
若:
int a=-5;
原码:10000000 00000000 00000000 00000101(把5的原码首位改成1)
反码:111111111 111111111 111111111 11111010(-5原码首位不变,其余的取反)
补码:111111111 111111111 111111111 11111011 (在反码的基础上+1)
(负数的原码,反码,补码各不相同)
(原码,补码,反码的首位称为符号位 ,符号位为0 -代表正数,1-代表负数)
int a = 5;
int b <<1 ;
b的值是a的补码向左移动一格,即:左边去掉一个数,右边补一个0;
b的补码:00000000 00000000 00000000 00001010
转化二进制数就是10
同理:
int a = 5 ;
int c >> 1 ;
c的值是a的补码向右移动一格,即:右边去掉一个数,左边补一个0;
c的补码:00000000 00000000 00000000 00000010
转化二级制就是2
从算术上说:左/右边丢弃,右/左边补原符号位
从逻辑上说:左/右边丢弃,右/左边补0
移位不会改变操作数的本身数值
注:移位操作符的操作数只能是整数。对应于移位操作符,不要移动负正数。
#include<stdio.h>
int main()
{
int a = 5;
int b = a << 1;
int c = a >> 1;
printf("%d\n", a); // 5
printf("%d\n", b); //10
printf("%d\n", c); //2
return 0;
}
3.位操作符
& //按(二进制)位与| //按(二进制)位或^ //按(二进制)位异或
#include<stdio.h>
int main()
{
int a = 3;
int b =-5;
int c = a & b;
printf("%d\n", c);
return 0;
}
先分别求出a和b的补码,&是与,二级制数一一对应,两个同为1则为1
先分别求出a和b的补码,|是或,二级制数一一对应,两者其中为1则为1
先分别求出a和b的补码,^是异或,二级制数一一对应,两者相同则为0,相异为1
不能创建临时变量(第三个变量),实现两个数的交换。
#include<stdio.h> int main() { int a = 3; int b = 5; printf("%d %d\n", a, b); //这是有第三个变量的 //int tmp = a; //a = b; //b = tmp; //printf("%d %d\n", a, b); //这是没有第三方变量 //a = a + b; //把a和b的和赋值给a //b = a - b; //此时a=8;b=a-b;b=3 //a = a - b; //此时a=8,b=3;a=a-b;a=5 //printf("%d %d\n", a, b); //但这种有缺陷,当a,b的数值过大,导致溢出,就会出现问题 a = a ^ b; b = a ^ b; a = a ^ b; printf("%d %d\n", a, b); //a=3; 二进制是011 //b=5; 二级制是101 //进行异或后就成立了 return 0; }
注意:异或操作符只能运用整数里。
4.赋值操作符
= 赋值操作符可以连续使用,从右向左赋值,但不能在定义变量时对变量进行连续赋值。
复合赋值:
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
5.单目操作符
! 逻辑反操作- 负值+ 正值& 取地址sizeof 操作数的类型长度(以字节为单位)~ 对一个数的二进制按位取反-- 前置、后置--++ 前置、后置++* 间接访问操作符(解引用操作符)(类型) 强制类型转换//*&a==>asizeof对于变量名可以省略后面的括号,但是对于数据类型不能省。
sizeof括号中放的表达式不参与运算。
EOF-->-1,则~EOF是0
前置++是先++再使用
后置++是先使用再++
6.关系操作符
>>=<<=!= 用于测试“不相等”== 用于测试“相等
7.逻辑操作符
&& 逻辑与|| 逻辑或
逻辑与,逻辑或只用来关心真假,按位与,按位或是用来二进制的计算。
逻辑与是两者都为真才行,逻辑或是有一个成立就可以。
#include <stdio.h> int main() { int i = 0, a = 0, b = 2, c = 3, d = 4; i = a++ && ++b && d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
逻辑与从左往右看,a=0;a++是后置,先使用,a++是0,所以为假,后面的不用看,最后打印出a=1,b=2,c=3,d=4。
8.条件操作符
exp1 ? exp2 : exp3
exp1如果为真,exp2执行
exp1如果为假,exp3执行
9.逗号表达式
exp1 , exp2 , exp3 , …expN
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
10.下标引用、函数调用和结构成员
int arr[10];
arr[9] = 10;
int add(int x,int y)
{
return x+y;
}
int main()
{
int c=add(2,3);
printf("%d",c);
return 0;
}
#include <stdio.h>
struct stu
{
char name[20];
char id[20];
int age;
};
int main()
{
struct stu b = {"xiaohong","C202115311103",55};
printf("%s %s %d",b.name,b.id,b.age);
struct stu * pb=&b;
printf("%s %s %d",(*pb).name,(*pb).id,(*pb).age);
//printf("%s %s %d", pb->name, pb->id, pb->age);
}
二.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
1.隐式类型转换
C 的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:表达式的整型运算要在 CPU 的相应运算器件内执行, CPU 内整型运算器 (ALU) 的操作数的字节长度 一般就是int的字节长度 ,同时也是 CPU 的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用 CPU ( general-purpose CPU )是难以直接实现两个 8 比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以, 表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int, 然后才能送入 CPU 去执行运算。
怎么整型提升呢?
// 负数的整形提升char c1 = - 1 ;变量 c1 的二进制位 ( 补码 ) 中只有 8 个比特位:1111111因为 char 为有符号的 char所以整形提升的时候,高位补充符号位,即为 1提升之后的结果是:11111111111111111111111111111111// 正数的整形提升char c2 = 1 ;变量 c2 的二进制位 ( 补码 ) 中只有 8 个比特位:00000001因为 char 为有符号的 char所以整形提升的时候,高位补充符号位,即为 0提升之后的结果是:00000000000000000000000000000001// 无符号整形提升,高位补 0
#include<stdio.h>
int main()
{
char a = 3;
//3的二进制补码是00000000000000000000000000000011
//提升后00000011
char b = 127;
//127的二进制补码00000000000000000000000001111111
//提升后01111111
char c = a + b;
//00000000000000000000000000000011
//00000000000000000000000001111111
//00000000000000000000000010000010
//c提升后10000010
//因为 char 为有符号的 char所以整形提升的时候,高位补充符号位,即为1提升之后的结果是:
//11111111111111111111111110000010
//11111111111111111111111110000001
//10000000000000000000000001111110
//即-126
printf("%d\n", c);
return 0;
}
2.算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为 寻常算术转换 。
long doubledoublefloatunsigned long intlong intunsigned intint
3.操作符的属性
1. 操作符的优先级2. 操作符的结合性3. 是否控制求值顺序。*两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
优先级: (优先级从上到下依次变小)
操作
符
|
名称
|
用法示例
|
结合
性
|
是否控制求值
顺序
|
---|---|---|---|---|
()
|
聚组
|
(表达式)
|
无
|
否
|
()
|
函数调用
|
rexp
(
rexp
,
...,rexp
)
| 从左到右 | |
[ ]
|
下标引用
|
rexp[rexp]
| ||
.
|
访问结构成员
|
lexp.member_name
| ||
->
|
访问结构指针成员
|
rexp->member_name
| ||
++
|
后缀自增
|
lexp ++
| ||
--
|
后缀自减
|
lexp --
| ||
!
|
逻辑反
|
! rexp
| 从右向左 | |
~
|
按位取反
|
~ rexp
| ||
+
|
单目,表示正值
|
+ rexp
| ||
-
|
单目,表示负值
|
- rexp
| ||
++
|
前缀自增
|
++ lexp
| ||
--
|
前缀自减
|
-- lexp
| ||
*
|
间接访问
|
* rexp
| ||
&
|
取地址
| &lexp | ||
sizeof
|
取其长度,以字节表示
|
sizeof rexp sizeof()
| ||
(类
型)
|
(
类型)
|
(类型
) rexp
| ||
*
|
乘法
|
xp * rexp
| 从左向右 | |
/
|
除法
|
rexp / rexp
| ||
%
| 取余 | rexp % rexp | ||
+
| 加法 | rexp + rexp | ||
- | 减法 | rexp - rexp | ||
<<
|
左移位
|
rexp << rexp
| ||
>>
|
右移位
|
rexp >> rexp
| ||
>
|
大于
|
rexp > rexp
| ||
>=
|
大于等于
|
rexp >= rexp
| ||
<
|
小于
|
rexp < rexp
| ||
<=
|
小于等于
|
rexp <= rexp
| ||
==
| 等于 | rexp == rexp | ||
!=
| 不等于 | rexp != rexp | ||
&
|
位与
|
rexp & rexp
| ||
^
|
位异或
|
rexp ^ rexp
| ||
|
|
位或
|
rexp | rexp
| ||
&&
|
逻辑与
|
rexp && rexp
| 是 | |
||
|
逻辑或
|
rexp || rexp
| ||
? :
|
条件操作符
|
rexp ? rexp : rexp
| 无 | |
=
| 赋值 |
lexp = rexp
| 从右向左 | 否 |
+=
|
以
...
加
|
lexp += rexp
| ||
-=
|
以
...
减
|
lexp -= rexp
| ||
*=
|
以
...
乘
|
lexp *= rexp
| ||
/=
|
以
...
除
|
lexp /= rexp
| ||
%=
|
以
...
取模
|
lexp %= rexp
| ||
<<=
|
以
...
左移
| lexp <<= rexp | ||
>>=
|
以
...
右移
| lexp >>= rexp | ||
&=
|
以
...
与
| lexp &= rexp | ||
^=
|
以
...
异或
| lexp ^= rexp | ||
|=
|
以
...
或
| lexp |= rexp | ||
,
| 逗号 | lexp,rexp | 从左向右 | 是 |
4.一些问题表达式
示例1:
a*b + c*d + e*f
代码 1 在计算的时候,由于 * 比 + 的优先级高,只能保证, * 的计算是比 + 早,但是优先级并不能决定第三个 * 比第一个 + 早执行。
a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f
或者:
a*b
c*d
e*f
a*b + c*d
a*b + c*d + e*f
示例2:
c + --c;
同上,操作符的优先级只能决定自减 -- 的运算在 + 的运算的前面,但是我们并没有办法得知, + 操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。如:如果已知c的值为2 先--c得1 2+1=3如果c的值还没准备好,先--c 得出值 (--c的值)+ --c 比如1+1=2