运算符:
用来代表某种运算的符号,我们在进行运算的时候,操作数是可能有几种的,你可能会有1个操作数, 2个操作数 ,3个操作数。
几目运算符:该符号带了几个操作数。①单目运算符:1个操作数 ②双目运算符:2个操作数 ③三目运算符:3个操作数
在运算符的时候有一个方向:我们叫结合性从左边到右边,从右边到左边。
a + b 与 b + a在数学上面是一样的,但是c语言里面有点不一样
a + b;
int a;
int b;
int c = a + b;// c = b + a;在大多数情况里面还是和数学一样
但是有特别的例子:(i++) + (i + j) != (i + j) + (i++)实际上这两个式子是不一样的。ps:!表示非,!=表示不等于
在运算符里面是有优先级的:谁先算谁后算
单目运算符 > 算术运算符 > 关系运算符 > 逻辑运算符 > 条件运算符 > 赋值运算符
> 逗号运算符
其实运算符我们可以不用特意去记,用得多了自然而然的就能分清了。
算术运算符:进行算术运算的符号
+ - * / % : 双目运算符
++ 和 -- :单目运算符
++和 --分为前后
这两个符号可以在我们的操作数的前后出现
在前面出现的时候我们叫前++/前--
后面出现的我们叫后++/后--
他们有区别:
在前面出现的时候:先变它的值,然后在取值
在后面出现的时候:先取值,然后再变它的值
int i = 3;
int a = ++i;//这时a = 4 i = 4
printf("%d",i);
int b = i++;//这时b = 4 i = 5
printf("%d",i);
int c = --i;//这时c = 4 i = 4
printf("%d",i);
int d = i--;//这时d = 4 i = 3
printf("%d",i);
printf("%d %d %d %d\n",a,b,c,d);//将abcd打印出来看看
双目算术运算符:
1 + 2;
/ : 在整数里面叫取整,小数里面叫除以。
% :两个操作数都必须是整数你才能操作,叫作求余。
5 / 4 : 取整,结果为1
5.0 / 4 :除以 结果1.25
int a = 5.0 / 4;
//结果1.25 会赋值给a,但是因为定义的a为整型,所以只保留整数部分,因此结果就是会将小数丢掉 为 1。于是a = 1。
int a = 5;
int b = 4;
double c = a / b; -> c最终的值为1.0
我这个时候是希望它变成除以,乘上一个小数就可以了
c = 1.0 * a / b;
我也可以叫计算机将a看成小数,强制转换 (类型)
c = (double)a / b;这时c的值是1.25;
c = (double)(a / b);这时c的值是1.0;这里相当于是先算将a / b取整,然后将结果看成是一个小数
打印出来看的时候记得是 %f ->打印浮点型
关系运算符,用来判断两者之间的关系的运算符:< > >= <= != ==
双目运算符,结合性:从左往右
关系运算符是为了表示两者之间的关系的,此关系如果成立,那么值就是1,此关系不成立,那么值就是0
5 > 4 :它的值是1
5 < 4 :它的值为0
在我们的数学里面有这种写法:5 > 4 > 3:数学里面是完全成立的但是c语言里面这个式子就会有问题
5 > 4 > 3:这个关系是不成立的
5 > 4:关系成立 那么值为1
1 > 3:关系不成立 值为0
printf("%d %d %d\n",5 > 4,5 > 4 > 3,5 > (4 > 3));//输出为:1 0 1
5 > 4 > 3在数学上面的思维在c语言里面也经常会遇到,我们需要在c语言里面表达数学上面的这种思维:5 > 4 并且 4 > 3,并且:我们需要表达这种思想。
表达这种并且的思想:逻辑运算,进行逻辑运算运算的符号我们叫逻辑运算符
逻辑运算符:
! :逻辑非 单目运算符 真的变成假的假的变成真的
||:逻辑或 双目运算符 从左往右 或者
&&:逻辑与 双目运算符 从左往右 并且
1)逻辑与 -- 用AB表示:当A,B都为1(真)时,其值为1,否则为零。
2)逻辑或 -- 用 A+B 表示:当A,B都为0(假)时,其值为0,否则为1。
3)逻辑非 -- 用 A上'¯'表示,当A=0时,A的非为1,A=1时,A的非为0。
逻辑运算连接的式子我们叫逻辑表达式,这个表达式里面有两种值
1,真值(非0即为真,1)
2,假值(0)
逻辑运算符的值为真的话,那么它的值就是1
1为真,但是这里真即为1是不对的,因为5也是个真的。比如:
5 && 4 :因为左边非0为真,右边也是非0为真,这个表达式的值为1。
int a = 3;
int b = 4;
a && b :值为 1
!a && b : 值为0
!a || b :值为 1
c语言中的惰性运算:
int a,b,c,d,e,f;
a = 1,b = 2,c = 3,d = 4;
e = 2,f = 2;
(e = a > b) && (f = c > d);
printf("%d %d\n",e,f);//0 0 实际结果为0 2
上述的例子里面后面的f = c > d这个东西没有算这是c语言里面的惰性运算:当c语言知道这个玩意儿已经是一个定局了,后面是多少,就可以不用关心了。
a && b && c:
当a为假了之后,后面的b c不管是真还是假,结果都是假的,这个时候就不会去计算b c,只有a为真才会去计算b ,之后只有a b都是真的才会去计算c。
a || b || c
当a为真的时候,后面b c不管是真还是假结果都是真的,因此就不会去计算 b c,只有当a为假的时候才会去计算b ,之后当 a b都是假的时候才会去计算c。
感兴趣可以做这个练习:
闰年的规则:(1)能被4整除并且不能被100整除的年份就是闰年,例如1900它不是闰年。(2)能被400整除的年份也是闰年
我们需要写一个逻辑表达式来判断你的年份是否是闰年,小提示整除的概念:a % b如果余数为0表示为整除,不为0表示不能整除,依据值来判断真假:0为假 非0即为真。
参考代码:
#include
int main()
{
int year,flag;//flag用来当做标记
printf("please input a year:\n");
scanf("%d",&year);
if( (year%4== 0) && ( (year%100)!=0 ) || year%400==0 )//能被4整除,但不能被100整除。或者能被400整除,flag=1
{
flag=1;
}
else//不是闰年,flag=0
{
flag=0;
}
if(flag==1)
{
printf("yes");
}
else
{
printf("no");
}
return 0;
}
位运算:
按照bit位来实现运算的一种运算符
&按位与
| 按位或
^按位异或
~按位取反
<<按位左移
>>按位右移
其中除了~按位取反是单目运算符以外,其他的都是双目运算符,结合性:从左往右,位运算在使用的时候只能是整数,在操作的时候必须将操作数变成bit,也就是一个bit对应一个bit来的。
~ :按位取反:将所有的bit位,1变成0 0变成1
int a = 1;(以前提过int为4个字节,一个字节8个bit)
00000000 00000000 00000000 00000001
~a =11111111 11111111 11111111 11111110
printf("%d\n",~a);11111111 11111111 11111111 11111110
~a是有符号的,因此要按照补码的形式进行转换
~a的最高bit是1,因此需要用负数的补码进行转换
先-1 得到 11111111 11111111 11111111 11111101
在取反 ~ 得到 00000000 00000000 00000000 00000010
添加- 号 得到最终值 -2
如果输出得是无符号的~a,那么printf("%u\n",~a);值为 2 ^ 32 - 2
char b = 1;
0000 0001
printf("%d\n",~b); ~b = 1111 1110
这时短赋长的问题解决,也就是前面的高位补符号位
11111111 11111111 11111111 11111110
结果跟上面还是一样,值为-2。
&:按位与,每一个bit都要参与运算
只有同时为高电平才会是高电平,只要有一个低电平了,那么就被拉低了
a = 3; 0000 0011
b = 5; 0000 0101
c = a & b; 0000 0001 得到c的值为 1
&------------
我需要将一个寄存器a,其它的bit不能动将bit3的功能关闭,bit3复位(将这个bit变成0),
以8位的寄存器来实现,bit位是从0开始的,将第nbit复位,其他的不变
a寄存器: xxxx xxxx
xxxx xxxx
&
1111 0111
------------------
xxxx 0xxx
x & 0 = 0;
x & 1 = x;
最终的操作 a = a & 0xf7 // a = a & (~(0x08))
a = a & (~(0x08))
-> a = a & (~(0x01 << n))
现在有一个变量 a = 0xff;它与上1的结果位:a & 1 -> 1111 1111 & 00000001 -> 1
假定a是一个字节,a = a & 0xff
| :按位或
基本规则:只要有高电平就是高电平
只有全部都是低电平才是低电平
我需要将一个寄存器a,其它的bit不能动,将bit3的功能打开,bit3置位(将这个bit变成1)以8位的寄存器来实现,bit位是从0开始的。
将第nbit置位:
a = a | 0x08;// a | 0000 1000
a = a | (0x01 << n);
一般都是1为功能打开,0为功能关闭
但是有特殊的情况,如:led灯有共阳(共阳极),共阴(共阴极)区别,共阳:1为灭 0为亮,共阴:1为亮 0为灭
^ : 按位异或,每一个bit都要操作
基本规则:相同为0,不同为1
x y x ^ y
0 0 0
1 0 1
0 1 1
1 1 0
2 ^ 4 ->数学上面是2的4次方,可以这么写,但是c语言里面是完全不一样的,因此我们c语言里面的,求次方,必须通过函数。
我需要将一个寄存器a,其它的bit不能动,将bit3的功能置为相反(如果为0就让他变成1,如果是1就让他变成0。
根据上面的真值表可以看出
x ^ 0 = x,
x ^ 1 = ~x;
a = a ^ 0x08;
其他bit全部取反,bit3不动。
a = a ^ 0xf7;//a ^ (~(0x08))
我需要将一个寄存器a,其它的bit不能动,第nbit取反
a = a ^ (0x01 << n);
练习:
有变量a b两个,可以用scanf进行输入
int a,b;
scanf("%d%d",&a,&b);//在终端输入的时候以空格或者回车做分隔
输入方式一:123 456 回车
方式二:123 回车 456 回车
不通过这个中间变量实现a b互换
假设a = 3 b = 4;
不知道你经过了一个什么操作
a = 4;
b = 3;
三种交换值的方法;代码奉上
#include
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("a = %d b = %d\n",a,b);//这里是在做测试 看你的输入是否正确
/* (1) 带中间变量的交换两个变量的值
int t;
t = a;
a = b;
b = t;
*/
/* (2)不带中间变量
这个方法是有bug的,
a b的值可能会足够的大
当两个值的和超过了其中一个变量的内存的最大值
那么就会溢出,溢出之后就错了
b = a + b;//b = 3 + 4 = 7;
a = b - a;//a = b - a = 7 - 3 = 4;
b = b - a;//a = b - a = 7 - 4 = 3;
*/
//(3)异或交换值
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d b = %d\n",a,b);
return 0;
}
a b在内存里面的状态都是bit
当a b的bit全部能互换位置,那么a b本身就互换了
x y x=x^y y=x^y x=x^y
0 0 0 0 0
1 1 0 1 1
0 1 1 0 1
1 0 1 1 0
<< :按位左移将前面的操作数左移后面制定的bit数
左移的规则:左移之后,右边空出来的bit自动变成0,也就是右边全部补0,当左边溢出的全部是0,并且不看符号,那么左移。
n个bit,那么实际上可以看出将这个数扩大
2 ^ n倍(2的n次方倍)
如:
a << 3;//将a左移3个bit
a = 5; //0000 0101
a = a << 3;//
a = 40
左移有两种:算术左移 逻辑左移,他们没有区别,左移之后,右边空出来的bit自动变成0,也就是右边全部补0。
>> :按位右移:分为逻辑右移以及算术右移。
逻辑右移:右边移出去的bit位直接丢弃,左边不管是有符号,还是没有符号都补0。
算术右移:右边移出去的bit位直接丢弃,左边如果是有符号的,左边补符号位,如果是没有符号的,左边补0
gcc/vc编译器默认有符号的时候用的是算术右移,这个时候就可能会出现结果不匹配的问题,c语言建议:将一个有符号的数进行右移的时候,为了保证,结果的一致性,请将这个有符号的强制转换成无符号的:
char a = 0x34;
a = a >> 3;//这个东西最好是不要做
//正常的做法 a = (unsigned char)a >> 3;
思考一个问题:
我有一个寄存器(8bit)a;,我要确定第3bit上面的功能是否打开,实际上就是要确定第3bit上面是1还是0,我们判断之前要知道一个事情:我能判断的只有真或者假
假的值为0 真的值为非0
当为假的时候我能确定所有的bit都是0
当为真的时候我是不能确定哪些bit是1的
因此我只能将我要判断的那个bit保留,其它的我要全部置0
a & (0x01 << 3) ->判断这个玩意儿的值是真的还是假的
如果是真值那么这个bit就是1
如果是假值那么这个bit就是0
我需要第3、4bit同时置位,a = a | (0x03 << 3)
赋值运算符:双目运算符 这个玩意儿的结合性是从右往左
赋值运算表表达式本身也会有自己的值,它的值为右边的值
a = b
a = b = 6;-> a = (b = 6)//b = 6本身是一个表达式 它也有自己的值 值为6
a = 6
这个表达式做完a b的值都是6
if(a == b)与if(a = b)的区别:
if(a == b)//如果a和b的值相等
if(a = b)//将b的值赋值给a,然后如果b为假/真
我们经常都是要表达如果a的值等于b那么记得一定是用a==b,不要搞混淆了
=号的左边是一个可写的地址,我们叫左值,=号的右边是一个可读的地址,我们叫右值
右值可以直接是常量等值,但是左值不行,它必须代表一个可写的地址
a = -1;
3 = a;//3不会是一个可写的地址 因此这个玩意儿语法有问题,错误。
我们的项目里面经常会判断两个东西的值是否相等:
a == b 用来判断两个东西的值是否相等的表达式
a = b;//合法 但是这个表达式是一个赋值表达式
if(a == b)
{}正确的
if(a = b)
{}也是正确的
上面的式子里面b是可以为常量的,因此我的代码就可以变成
if(a == 1)
{}正确的
if(a = 1)
{}也是正确的
我们在写代码的时候经常会将==少写一个,然后代码的问题就找不出来了,为了保证这种问题不出现
建议将上面的语句变成
if(1 == a)
{}正确的
你的等号又少写了一个
那么你的代码就会变成
if(1 = a)
{}不正确的 1不能是左值 这个时候编译的时候就直接报错
条件运算符: ? : 三目运算符
语法构成:
表达式1 ? 表达式2 : 表达式3;
如果表达式1的值为真,则整个表达式的值为表达式2的值
如果表达式1的值为假,则整个表达式的值为表达式3的值
练习:
输入两个整数,判断两个数谁大,输出大的那个值
max = (a > b ? a : b);
输出三个中的大值
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int max = a > b ? a : b;//取得两个之间的大值
//ab中间的大的值和c取比较
max = max > c ? max : c;//比较完 max就保存了abc中间的大的值
max = (max = (a > b ? a : b)) > c ? max : c;
a = 4 < 5 ? 5 > 6 : 7;//最终a的值为0(5 > 6的值)
逗号表达式:用,号连接在一起的表达式,双目运算符,优先级是最低的,从左往右结合;
表达式1,表达式2,表达式3,......表达式n,求值顺序为:先算表达式1,再算表达式2,再算表达式3.....最后算表达式n,逗号表达式本身是一个表达式,因此它这个表达式也会有值,它的值为表达式n的值,也就是最右边的那个表达式的值。
int a = 3,b = 4;
int c ;
c = a = (b = 6,5 + 6);//经过这个表达式之后abc的值
// a = 11 b = 6 c = 11
指针运算符:
*(指针); &(取地址)
求字节运算符
sizeof;
求它后面的那个玩意儿在内存里面所占的字节数
int a;
sizeof(a) ->看a在内存里面占几个字节,结果为4
练习:
写一个表达式,求a占的bit数,在我们的实际单片机操作里面,有的单片机内存不足,因此int有的时候只有2个字节,但是绝大多数的时候都是4个字节,现在我有如下代码:
int a;
int n = sizeof(int) * 8;//n为多少?
分量运算符:结构体天天用的
. 和 ->
下标运算符:
[] 在数组里面会看见
强制转换运算符:
语法构成:
(类型)表达式;
int a = 3;
char b = (char)a;//在c语言里面类型不匹配是不能赋值的
函数调用运算符:
做函数调用 scanf printf
小结:
表达式:在c语言里面就是为了表达某一个意思的式子,用运算符连接起来的式子我们就叫表达式
在c语言里面每一个表达式都会有值,这个值分为两种
1 真值:非0即为真
2 假值:0
特别是那种判断大小的,逻辑表达式...当判断为真的时候它的值是1
a > b : 这个表达式的值就只有两种
要么是成立:1
要么就是不成立:0
c语言里面的惰性运算一定要注意,当我们能确定这个玩意儿的最终的值了
那么就不会往后面算了。