P5.运算符
运算符介绍:
运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。
1) 算术运算符 (+, -, * , / , %)
2) 赋值运算符 (= += -= ..)
3) 关系运算符(比较运算符)(比如 > >= < <= == 等等)
4) 逻辑运算符 (&& 逻辑与 || 逻辑或 ! 逻辑非)
5) 位运算符 (& 按位与 | 按位或 ^ 按位异或 ~ 按位取反等等)
6) 三元运算符 ( 表达式 ? 表达1 : 表达2)
算术运算符:
介绍:算术运算符是对数值类型的变量进行运算的,在C程序中使用的非常多。

案例演示:案例演示算术运算符的使用(operator.c)。
1) +, - , * , / , %, ++, -- , 重点讲解 /、%、++
2) 自增:++ 作为独立的语句使用: 前++和后++都完全等价于 i=i+1; 作为表达式使用 前++:++i先自增后赋值 后++:i++先赋值后自增
3) -- ,+、-、* 是一个道理,完全可以类推。
细节说明:
1) 对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数 部分而舍弃小数部分。 例如:int x= 10/3 ,结果是 3
2) 当对一个数取模时,可以等价 a%b=a-a/b*b , 这样我们可以看到 取模的一个本质 运算。 3) 当 自增 当做一个独立语言使用时,不管是 ++i; 还是 i++; 都是一样的,等价
4) 当 自增 当做一个 表达式使用时 j = ++i 等价 i = i + 1; j = i;
5) 当 自增 当做一个 表达式使用时 j = i++ 等价 j = i; i = i + 1;

自增,自减课堂练习:
void main() {
int i1 = 10, i2 = 20;
int i = i1++;//i =10, i1=11
printf("i=%d\n",i);// 10
printf("i1=%d\n", i1);//11
i = ++i1;
printf("i=%d\n", i);//12
printf("i1=%d\n", i1);//12
i = i2--;
printf("i=%d\n",i);// 20
printf("i2=%d\n",i2);//19
i = --i2;
printf("i=%d\n", i);//18
printf("i2=%d\n", i2);//18
getchar();
}
输出结果:
i= 10 i1= 11
i= 12 i1= 12
i= 20 i2= 19
i= 18 i2= 18

课堂练习2:
1) 假如还有97天放假,问:xx个星期零xx天
2) 定义一个变量保存华氏温度,华氏温度转换摄氏温度的公式为:5/9*(华氏 温度-100),请求出华氏温度对应的摄氏温度。


我的源码:
/*
练习要求:
1.假如还有99天放假,问:还有xx个星期零xx天
2.定义一个变量保存华氏温度,华氏温度转摄氏温度的公式:5/9 * (华氏温度-100),请求出华氏温度对应的摄氏温度。
*/
#include <stdio.h>
void main(){
//1思路:
//1.定义一个变量 days 保存天数
//2.定义一个变量 week 保存计算得到的星期
//3.定义一个变量 leftDays 保存剩余天数
//4.要使用 % 和 / 来计算
short days = 99;
short week = days / 7;
short leftDays = days % 7;
//2思路略过
double huaShi = 13;
double sheShi = 5.0 / 9 * (huaShi - 100);// 5.0/9,不然输出都是0(5/9等于0)
printf("还有%d个星期零%d天放假\n",week,leftDays);
printf("华氏温度%.3f转为摄氏温度是%.3f\n",huaShi,sheShi);
getchar();
}

关系运算符(比较运算符)
介绍:
1) 关系运算符的结果 要么是真(非0 表示),要么是 假(0 表示)
2) 关系表达式 经常用在 if结构的条件中或循环结构的条件中

细节说明:
1) 关系运算符的结果要么是真(非0 表示, 默认使用1),要么是 假(0 表示)
2) 关系运算符组成的表达式,我们称为关系表达式。 a > b
3) 比较运算符 "=="(关系运算符) 不能误写成 "=" (赋值)

我的源码:
/*
关系运算符 也叫 比较运算符
1.关系运算符的结果 要么是真(非0表示,默认使用1表示),要么是假(用0表示)
2.关系运算符组成的表达式,我们称为关系表达式。如 a > b
3.比较运算符 ==(关系运算符) 不能写成 =(赋值)
*/
#include <stdio.h>
void main(){
int a = 13;
int b = 3;
printf("关系表达式 a>b 的值是%d\n",a > b);//1
printf("关系表达式 a>=b 的值是%d\n",a >= b);//1
printf("关系表达式 a<b 的值是%d\n",a < b);//0
printf("关系表达式 a<=b 的值是%d\n",a <= b);//0
printf("关系表达式 a==b 的值是%d\n",a == b);//0
printf("关系表达式 a!=b 的值是%d\n",a != b);//1
getchar();
}

逻辑运算符:
介绍:用于连接多个条件(一般来讲就是关系表达式),最终的结果要么是真(非0 表示),要么是 假(0 表示) 。





练习题2:请写输出结果
void main() {
int x=1;
int y=0;
short z=42;
if((z++==42)&&(y=1)) { // z = 43 , y =1
z++; // z = 44
}
if((x=0) || (++z==45)) { // z = 45
z++; // z = 46
}
printf("z=%d", z); // z 为46
getchar();
} 结果为:z 为 46

我的源码:
/*
逻辑运算符 (&& || !三个)
用于连接多个条件(一般来讲就是关系表达式),最终结果要么是真(非0表示),要么是假(0表示)
*/
#include <stdio.h>
void main(){
//int a = 10,b=99;
//if(a<20 && b++>100){
// printf("OK100");//不会输出
//}
//printf("b=%d\n",b);//输出b=100
//int a = 10,b=99;
//if(a<2 && ++b>99){
// printf("OK100\n");//也不会输出
//}
//printf("b=%d\n",b);//输出b=99
//1.在就行 && 操作时,如果第一个条件为false,则后面的条件不再判断,整个结果为false
//2.该现象称为 短路现象,所以逻辑与 也称为 短路逻辑与
//int a = 10,b=99;
//if(a<5 || b++>100){
// printf("@@@OK100@@@\n");//不会输出
//}
//printf("b=%d\n",b);//输出b=100
//int a = 10,b=99;
//if(a>5 || b++>100){
// printf("@@@OK100@@@\n");//输出@@@OK100@@@
//}
//printf("b=%d\n",b);//输出b=99
//1.在就行 || 操作时,如果第一个条件为true,则后面的条件不再判断,整个结果为真
//2.该现象称为 短路现象,所以逻辑或 也称为 短路逻辑或
//逻辑非
int score = 100;
int res = score < 99;//res=0
if(res){
printf("Hello Tom\n");
}
if(!res){
printf("Hello Jack\n");
}
getchar();
}

#include <stdio.h>
void main(){
int x=1;
int y=0;
short z=42;
//下面if语句的 赋值语句写在前面不需要括号,写在后面需要括号。优先级问题
if(z++==42 && (y=1)){//z=43,y=1
z++;//z=44
}
if(x=0 || ++z==45){//x=0为假,z=45
z++;//z=46
}
printf("z=%d\n",z);
getchar();
}

赋值运算符:
介绍:赋值运算符就是将 某个运算后的值, 赋给指定的变量。 赋值运算符一览表 先讲 =、+=、-=、 *= 、/=、%=
<<=、>>=、&=、^=、 |= 和位运算相关,放 在后面讲解
运算符 描述 实例
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给
+= 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左 边操作数 C += A 相当于 C = C + A
-= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左 边操作数 C -= A 相当于 C = C - A
*= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左 边操作数 C *= A 相当于 C = C * A /= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左 边操作数 C
/= A 相当于 C = C / A %= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A
<<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
|= 按位或且赋值运算符 C |= 2 等同于 C = C | 2

案例演示:
案例演示赋值运算符的基本使用。
1) 赋值基本案例 [int num1 = 10 + 3]
2) 有两个变量,a和b,要求将其进行交换,最终打印结果
3) += 的使用案例
赋值运算符特点:
(1) 运算顺序从右往左
(2) 赋值运算符的左边 只能是变量,右边 可以是变量、表达式、常量值
(3) 复合赋值运算符等价于下面的效果 比如:a+=3;等价于a=a+3;

我的源码:
/*
赋值运算符:
就是将某个运算后的值,赋给指定的变量
有:= +=... <<=...
简单略过,
<<= 左移且赋值运算符 (c<<=2 等同于 c=c << 2)
>>= 右移且赋值运算符
&= 按位与且赋值运算符
^= 按位异或且赋值运算符
|= 按位或且赋值运算符(c |=2 等同于 c = c | 2)
*/
/*
运算顺序 从右往左
赋值运算符 左边只能是变量,右边可以是变量,表达式,常量值
注意:在运算时,可能截断小数点后面的部分
*/
#include <stdio.h>
void main(){
//int a1 = 10;
//int b1 = 11.1;
//a1+=1.7;
//printf("a1=%d b1=%d\n",a1,b1);//a1=11 b1=11
//有两个变量,a和b,要求进行交换,打印结果
int a = 13;
int b = 1313;
int temp = a;
a = b;
b = temp;
printf("a=%d b=%d\n",a,b);//a=1313 b=13
getchar();
}

位运算符
介绍:位运算符作用于位, 并逐位执行操作。 位运算符一览表 要彻底搞懂位运算, 需要先学习二进制, 因此位运算,我们 放在二进制后讲解
运算符 描述 实例
& 按位与操作,按二进制位进行"与"运算。运算规则: 0&0=0; 0&1=0; 1&0=0; 1&1=1;
(A & B) 将得到 12,即为 0000 1100
| 按位或运算符,按二进制位进行"或"运算。运算规则: 0|0=0; 0|1=1; 1|0=1; 1|1=1;
(A | B) 将得到 61,即为 0011 1101
^ 异或运算符,按二进制位进行"异或"运算。运算规则: 0^0=0; 0^1=1; 1^0=1; 1^1=0;
(A ^ B) 将得到 49,即为 0011 0001
~ 取反运算符,按二进制位进行"取反"运算。运算规则: ~1=0; ~0=1;
(~A ) 将得到 -61,即为 1100 0011, 一个有符号二进制数的补码形式。
<< 二进制左移运算符。将一个运算对象的各二进制位 全部左移若干位(左边的二进制位丢弃,右边补0)。 A << 2 将得到 240,即为 1111 0000
>> 二进制右移运算符。将一个数的各二进制位全部右 移若干位,正数左补0,负数左补1,右边丢弃。 A >> 2 将得到 15,即为 0000 1111

我的源码:
/*
进制介绍
1.二进制:0,1,满2进1,C语言中没有二进制常数的表示方法。
2.十进制:0~9,满10进1。
3.八进制:0~7,满8进1。以数字0开头。
4.十六进制:0~9和A~F,满16进1。以0x或0X开头表示。此处A~F不区分大小写。【A->10,b->11,...f->15】
--0x13bF + 1 ==0x13C0
*/
/*
进制转换:
其他进制转为十进制:0x1dcf==>1*16^3 + d*16^2 + c*16 + f = 7631。其他一样,位数 * 位权
十进制转为其它进制:用短除法,比如十进制->十六进制【一直除以16,直到商为0,再把余数倒过来写】
二进制转为8、16进制:3位二进制对应1wei八进制,4位二进制对应1位十六进制【把它算出来,位*位权 对应 写成一个数】
11 100 101-->八进制--> 3 4 5
11 1001 0110-->十六进制--> 3 9 6
八、十六进制转为二进制:和二进制转为8、16进制类似,反过来看就行
01230-->二进制-->001 010 011 000
0xAB29-->二进制-->1010 1011 0010 1001
*/
/*
【原码、反码、补码】
1.二进制的最高位是符号位,0表示正数,1表示负数
2.正数的原码、反码、补码都一样(三码合一)
3.负数的反码 = 它的原码符号位不变,其它位全部取反(0-->1,1-->0)
4.负数的补码 = 它的反码 + 1
5.0的反码,补码都是0
6.在计算机运算的时候,【都是以补码的方式来运算的】。但是最终结果显示,需要把补码转为原码
*/
/*
【位运算当然和逻辑运算符 不一样】
按位与(&),规则:两边都为1,才为1【0&0=0,1&1=1,0&1=0,1&0=0】
按位或(|),规则:两边有1为1【0|0=0,1|1=1,0|1=1,1|0=1】
按位异或(^),规则:两边相同为0,不同为1【0^0=0,1^1=0,0^1=1,1^0=1】
按位取反(~),规则:两边取反【~1=0,~0=1】
【最终显示是原码,补码最终都要转为原码】【一句话补码计算,原码最终显示】
/*
/*
位运算例子:【int有4个字节,下面是一个字节的【懒得补完整】,算出结果都一样】
~2 = -3;//二进制为 0000 0010 -->反码 0000 0010 -->补码 0000 0010 --》对补码取反 1111 1101--》还原为原码 1000 0011--》-3
~-2 = 1;//二进制为 1000 0010 -->反码 1111 1101-->补码 1111 1110--》对补码取反 0000 0001--》还原为原码 0000 0001--》1
~-5 = 4;//二进制为 1000 0101 -->反码 1111 1010-->补码 1111 1011--》对补码取反 0000 0100--》还原为原码 0000 0100--》4
2 & -3 = 0;//2的二进制为 0000 0010(补码一样)。-3的二进制为 1000 0011(补码 1111 1101)--> 0000 0010 & 1111 1101 =》0000 0000-->还原为原码 0
2 | 3 = 3;//2的二进制为 0000 0010(补码一样)。3的二进制为 0000 0011(补码一样)--> 0000 0010 | 0000 0011 =》0000 0011-->还原为原码(正数原码一样)--》3
2 ^ 3 = 1;//2的二进制为 0000 0010(补码一样)。3的二进制为 0000 0011(补码一样)--> 0000 0010 ^ 0000 0011 =》0000 0001-->还原为原码(正数原码一样)--》1
*/
/*
二进制左移(<<),规则:将二进制全部左移若干位【右边补0,左边二进制位丢弃】
二进制右移(>>),规则:将二进制全部右移若干位【(正数左边补0,负数左边补1),右边丢弃】(补0和1 是因为,不能改变数的符号)
【位运算说明2】
<< 叫算术左移。规则:符号位不变,低位补0
>> 叫算术右移。规则:低位溢出,符号位不变,并用符号位 补溢出的高位。
*/
/*
例子2://照样4个字节,只写其中的1个字节
1 >> 3 // (正数相当于除以3个2-->1/2/2=0)【左移就是乘3个2,2是权重,但是负数不能这样算,因为负数补码和原码不一样】
int a = 1 >> 2;//1的补码 0000 0001-->右移2位--> 0000 0000--》0
int b = -1 >> 2;//-1的补码 1111 1111-->右移2位-->1111 1111-->原码 1000 0001--》-1
int c = 1 << 3;// 0000 0001-->左移3位--> 0000 1000--》8【相当于1*2*2*2=8】
int d = -3 << 3;//-3补码1111 1101--左移3位-->1110 1000-->原码 1001 1000 --》-24
~6;
~-7;
13 & 7;
5 | 4;
-3 ^ 3;
*/
//1000 0000 0000 0001
//反码:1111 1111 1111 1110
//补码:1111 1111 1111 1111
#include <stdio.h>
int main(){
//int num = 011;
//int num2 = 0X13bf + 1;
//printf("num=%d num2=%d",num,num2);//num=9 num2=5056
int num1 = ~2;//-3
int num2 = ~-2;//1
int num3 = ~-5;//4
int num4 = 2 & -3;//0
int num5 = 2 | 3;//3
int num6 = 2 ^ 3;//1
int a = 1 >> 2;//0
int b = -1 >> 2;//-1
int c = 1 << 3;//8
int d = -3 << 3;//-24
printf("num1=%d num2=%d num3=%d num4=%d num5=%d num6=%d\n",num1,num2,num3,num4,num5,num6);
printf("a=%d b=%d c=%d d=%d",a,b,c,d);
getchar();
}

三元运算符
基本语法:条件表达式 ? 表达式1: 表达式2;
1) 如果条件表达式为非0 (真),运算后的结果是表达式1;
2) 如果条件表达式为0 (假),运算后的结果是表达式2;
3) 口诀: 一灯大师 =》 一真大师
使用细节:
1) 表达式1和表达式2要为可以赋给接收变量的类型(或可以自动转换), 否则 会有精度损失
2) 三元运算符可以转成if--else 语句
int res = a > b ? a++ : --b; // if ... else ..
课堂练习:
案例1:实现两个数的最大值 (a , b )
int a = 10;
int b = 99;
int res = a > b ? a++ : b--;
案例2:实现三个数的最大值
int a = 10;
int b = 100;
int c = 199;
int max = a > b ? a : b

运算符优先级
运算符优先级小结:
1) 结合的方向只有三个是从右向左,其余都是从左向右
2) 所有的双目运算符中只有赋值运算符的结合方向是从右向左
3) 另外两个从右向左的结合运算符,一个是单目运算,还有一个是三目运算()
4) 逗号的运算符优先级最低
5) 说一下优先级的大概的顺序 算术运算符 > 关系运算符 > 逻辑运算符(逻辑 非! 除外) > 赋值运算符 > 逗号运算符
6) 注意不需要刻意的记, 常用就慢慢有印象
优先级问题 表达式 经常误认为的结果 实际结果 (红色标注)
. 的优先级高于 *(-> 操作符 用于消除这个问题) *p.f p 所指对象的字段 f,等价于: (*p).f 对 p 取 f 偏移,作为指针, 然后进行解除引用操作,等 价于: *(p.f)
[] 高于 * int *ap[] ap 是个指向 int 数组的指针, 等价于: int (*ap)[] ap 是个元素为 组,等价于: int *(ap [])
函数 () 高于 * int *fp() fp 是个函数指针,所指函数 返回 int,等价于: int (*fp)() fp 是个函数,返回 价于: int* ( fp() )
== 和 != 高于位操作 (val & mask != 0) (val &mask) != 0 val & (mask != 0)
== 和 != 高于赋值符 c = getchar() != EOF (c = getchar()) != EOF c = (getchar() != EOF)
算术运算符高于位移运算符 msb << 4 + lsb (msb << 4) + lsb msb << (4 + lsb)
逗号运算符在所有运算符中 优先级最低 i = 1, 2 i = (1,2) (i = 1), 2
运算符优先级注 意事项和细节:
上表中,优先级同 为1 的几种运算符 如果同时出现,那 怎么确定表达式的 优先级呢?这里我 们专门说明一把
我的源码:
/*
位运算后面讲
三元运算符,基本语法:
1.如果 条件表达式 为非0(真),运算后的结果是 表达式1
2.如果 条件表达式 为0(假),运算后的结果是 表达式2
*/
/*
细节:
1.表达式1和表达式2 要为 可以赋给接收变量的类型(或可以自动转换),否则会有精度损失
2.三元运算符可以转换为 if-else语句
*/
#include <stdio.h>
void main(){
//int a = 13;
//int b = 1313;
//int c = 131313;
//int max = a > b ? a : b;
//int max2 = (a>b?a:b)>c?(a>b?a:b):c;//建议使用中间变量max写,简短一点
//
//printf("两个数的最大值是 %d\n",max);//1313
//
//printf("三个数的最大值是 %d\n",max2);//131313
int a =13;
int b = 1313;
int res = a > b ? a++ : b--;
int n1 = a > b? 1.1 : 1.2;//警告:从“double”转换到“int”,可能丢失数据
printf("\na=%d b=%d res=%d",a,b,res);//a=13 b=1312 res=1313
getchar();
}
/*
运算符优先级 (不知道的就查表)
运算符优先级小结
1.结合方向只有3个从右向左,其余都是从左往右
2.所有双目运算符(就是两个操作数那种 + - * /...),只有赋值运算符是 从右向左
3.另外两个 从右向左的运算符,一个是单目运算,还有一个是三目运算
4.逗号的运算符优先级最低
5.大概顺序:算数运算符 > 关系运算符 > 逻辑运算符(逻辑非(!) 除外) > 赋值运算符 > 逗号运算符
---同1级别优先级时:. 高于 *(指针取值的*),[] 高于 *,函数() 高于 *,==和!=高于位操作、也高于赋值符,算术运算符 高于 位移运算符,逗号在所有运算符优先级最低
6.不需要刻意背,多用就有印象了
*/