1.操作符的分类
~算数操作符:+、-、*、/、%
~移位操作符:<<、>>
~位操作符:&、|、^
~赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
~单目操作符:!、++、==、&、*、+、-、~、sizeof、(类型)
~关系操作符:>、>=、<、<=、==、!=
~逻辑操作符:&&、||
~条件操作符:?:
~逗号表达式:,
~下标引用:[]
~函数调用:()
~结构成员访问:.、->
操作符中有一些操作符和二进制有关系,需要用到二进制有关的内容
2.二进制和进制转换
二进制、八进制、十六进制是数值的不同表现形式
十进制满10进1,二进制满2进1
2.1二进制转十进制
二进制的权重为2^0,2^1,2^2
按照权重对二进制的数进行运算
2.1.1十进制转二进制数
将十进制数除以2,由上往下依次所得的余数就是十进制转换出的二进制数
2.2二进制转八进制和十六进制
2.2.1二进制转八进制
从二进制序列中右边低位开始向左每3个二进制位会换算1个八进制位,剩余不够3个二进制位的直接换算
0开头的数字,会被当做八进制
2.2.2二进制转十六进制
从二进制序列中右边低位开始向左每4个二进制位会换算个十六进制位,剩余不够4个二进制位的直接换算
十六进制表示数字的时候需要在前面加上0x
3.原码、反码、补码
整数的二进制表示方法有3种,即原码、反码和补码
这三种表示方法中均有符号位和数值位,二进制序列中,最高位是被当作符号位,剩余都是数值位
符号位用‘0’表示正,用‘1’表示负
正数的原、反、补码都相同
负数的三种表示方法各有不同
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码
补码:反码+1就得到补码
补码得到原码的方式也是可以使用:取反,+1
对于整型来说:数据在内存中存放的实际是补码
4.移位操作符
<<左移操作符
>>右移操作符
操作符的操作数只能是整数
4.1左移操作符
移位规则:左边抛弃、右边补零
4.2右移操作符
移位规则:
首先右移运算符分两种
1.逻辑右移:左边用0填充,右边丢弃
2.算数右移:左边用原该值的符号位填充,右边丢弃
注:对于移位操作符,不要移动负数位,这个是标准未定义的
5.位操作符
位操作符有:&(按位与)、|(按位或)、^(按位异或)、~(按位取反)
注:他们的操作数必须是整数
&(按位与):对应两串二进制序列都为1时,结果位才是1,否则为0
|(按位或):对应两串二进制序列都为0时,结果位才是0,否则为1
^(按位异或):对应两串二进制序列相同时结果为0,不同时结果为1
~(按位取反):将二进制序列中的1变为0,0变为1
一道面试题:不能创建临时变量,实现两个整数的交换
int main()
{
int a = 10;
int b = 20;
int c = a;
a = b;
b = c;//这种方法需要创建临时变量
a = a + b;
b = a - b;
b = a - b;//这种方法在数据过大时无法进行计算
a = a ^ b;
b = a ^ b;
a = a ^ b;//推荐使用这种方法
printf("%d %d", a, b);
return 0;
}
练习1:求一个整数储存在内存中的二进制中1的个数
方法一:
int main()//此方法在n为负数时无法实现
{
int n = 13;
int count = 0;
while (n)
{
if (n % 2 == 0)
count++;
n = n / 2;
}
printf("%d", count);
return 0;
}
方法二:
int main()
{
int n = -1;
int count = 0;
int i = 0;
for (i = 0; i < 32; i++)
{
if (((n >> 1) & 1) == 1)
count++;
}
return 0;
}
方法三:
int main()//更为简便的方法
{
int n = 0;
scanf("%d", &n);
int count = 0;
while (n)
{
n = n & (n - 1);
count++;
}
printf("%d", count);
return 0;
}
练习二:写一个代码,判断n是否为2的次方数
int main()
{
int n = 0;
scanf("%d", &n);
if (n & (n - 1) == 0)
printf("yes");
else
printf("no");
return 0;
}
练习三:编写代码将13的二进制序列的第5位改为1,然后再改回0
int main()
{
int n = 13;
n |= (1 << (5 - 1));
printf("%d", n);
n &= (~(1 << (5 - 1)));
printf("%d", n);
return 0;
}
6.单目操作符
!、++、--、&、*、+、-、~、sizeof、(类型)这些都是单目操作符
7.逗号表达式
是用逗号隔开的多个表达式
从左向右依次执行,整个表达式的结果是最后一个表达式的结果
int main()
{
int a = 1;
int b = 2;
int d = 0;
int c = (a > b, a = b + 10, a, b = a + 1);
if(a=b+1,c=a/2,d>0)
printf("%d", c);
return 0;
}
8.下标访问[]、函数调用()
8.1[]下标引用操作符
操作数:一个数组名+一个索引值
8.2函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数
9.结构成员访问操作符
9.1结构体
结构体这种自定义的数据类型,可以让程序员创造出合适的类型来
9.1.1结构的声明
struct tag
{
member-list;
}variable-list;
9.1.2结构体变量的定义和初始化
struct B//变量的定义
{
char c;
int m;
};p1//声明类型的同时定义变量p1
struct Stu
{
char name[20];
int age;
struct B bb;//结构体嵌套定义
float score;
}s5, s6, s7;
int main()
{
struct Stu s1 = { "张三",20,{'q',100}//结构体嵌套初始化,95.5f };//初始化
struct Stu s2 = { .age = 10,.score = 98.5f,.name = "aj",.bb = {'q',20} };指定顺序初始化
struct Stu s3;
printf("%d %d", s1.age,s1.bb.m);
return 0;
}
9.2结构成员访问操作符
9.2.1结构成员的直接访问
通过操作符(.)进行访问
使用方式:结构体变量.成员名
9.2.2结构体成员的间接访问(后续讲)
10.操作符的属性:优先级、结合性
这两个属性决定了表达式求值的计算顺序
10.1优先级
优先级是指一个表达式如果包含多个表达式,哪个运算符应该优先执行
10.2结合性
如果两个运算符优先级相同,这时候就要看结合性
大部分运算符是左结合(从左到右执行),少数是右结合,比如赋值运算符
大概记住一下操作符的优先级
• 圆括号( () )
• ⾃增运算符( ++ ),⾃减运算符(- )
• 单⽬运算符( + 和 )
• 乘法( * ),除法( / )
• 加法( + ),减法( )
• 关系运算符( < 、 > 等)
• 赋值运算符(=)
由于圆括号的优先级最高,故可以使用它改变其他运算符的优先级

参考网站:https://zh.cppreference.com/w/c/language/operator_precedence
11.表达式求值
11.1整型提升
C语言中整型算术运算符总是至少以缺省(默认)整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
整型提升的方法:
1.有符号的整数提升是按照变量的数据类型的符号位来提升的
2.无符号整数提升,高位补0
11.2算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的类型转换为另一个操作数的类型,否则操作就无法进行,层次如下
1 long double
2 double
3 float
4 unsigned long int
5 long int
6 unsigned int
7 int
如果某个操作数的类型在上面的列表中靠后,那么首先需要转换为另外一个操作数类型后执行运算
11.3问题表达式解析
11.3.1表达式1
a*b+c*d+e*f
优先级不能决定第三个*比第一个+早执行
11.3.2表达式2
c+--c
无法得知c是--前还是--后的值
11.3.3表达式3
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
在不同的编译器中测出来的值不一样
11.3.4表达式4
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);// 输出多少?
return 0;
}
无法得知函数调用的先后顺序
11.3.5表达式5
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
这个代码中第一个+在执行的时候,第三个++是否执行这个是不确定的