---- 操作符分类+剖析----:
1.算术操作符
算术操作符:
**1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
1.对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
2.% 操作符的两个操作数必须为整数。返回的是整除之后的余数。**
2.移位操作符
2.1 左移操作符
使用规则:
左边抛弃,右边补0
2.2 右移操作符
使用规则:
逻辑移位 : 左边用0填充,右边丢弃。
算术移位 : 左边用原该值的符号填充
3.位操作符
位操作符有:(这里的位都是二进制位)
这里都是看二进制位喔
& //按位与 0和0还是 0 , 1和0还是0 , 必须两个都为1 才能结合出来1 | //按位或 只要有一个1就能结合出来1啦,而只有;两个0 才能结合出来0 ^ //按位异或 相同为0 相异为1 注:他们的操作数必须是整数。
#include <stdio.h> int main() { int a = 1; int b = 2; a & b; a | b; a ^ b; return 0; }
给大家介绍一个比较神奇的代码,它是不创建变量,然后将a,b内容交换。用的正是这里介绍的按位异或^操作符
#include <stdio.h> int main() { int a = 10; int b = 20; a = a^b; //把a^b 看成一个钥匙,它遇到a就能打开b的然后变成b; b = a^b;//a相当于钥匙,它遇到b就打开b变成a,然后再赋给b;b就变成a的值了 a = a^b;//b相当于钥匙,它遇到a就打开a变成b,然后再赋值给a,a值就变成了b值 printf("a = %d b = %d\n", a, b); return 0; }
编写代码实现: 求一个整数存储在内存中的二进制中1的个数
#include <stdio.h> int main() { int num = 10; int count= 0;//计数 while(num) { if(num%2 == 1) count++; num = num/2;//正常思想从如何分解一个数的各个位数,,%10 再/10; } printf("二进制中1的个数 = %d\n", count); return 0;
另一种方法:
#include <stdio.h> int main() { int num = -1; int i = 0; int count = 0;//计数 for(i=0; i<32; i++) { if( num & (1 << i) ) count++; } printf("二进制中1的个数 = %d\n",count); return 0; }
还有没有更好的方法呢?
int main() { int a = -1; int count = 0; //怎么更好的计算a的1 的个数呢? // a & (a - 1);这种方式可以每次把最右边的1给拿掉; while (a) { a = a&(a - 1); count++; } printf("1的个数:%d", count); return 0; }
但这种方法实在想不到喔…
4.赋值操作符
看好是一个 = 而不是两个==喔
/
int a=10;//可以将想要的数赋值 int b=a+10;
复合赋值符
+=
-=
*=
/=
%=
<<=
&=
|=
^=
5.单目操作符
单目操作符介绍
! 逻辑反操作 - 负值 + 正值 & 取地址 sizeof 操作数的类型长度(以字节为单位) ~ 对一个数的二进制按位取反 -- 前置、后置-- ++ 前置、后置++ * 间接访问操作符(解引用操作符) (类型) 强制类型转
1. sizeof是用来计算变量所占空间的大小 ,它是一个操作符,不是库函数喔。
void test1(int arr[]) { printf("%d\n",sizoeof(arr)); } void test2(char ch[]) { printf("%d\n",sizeof(ch[]); } int main() { int a = 10; char s = 'a'; int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(a)); //一个整形4个字节 printf("%d\n", sizeof(s));//1一个字符1个字节 printf("%d\n", sizeof(arr));//数组10个整形40个字节 printf("%d\n", sizeof(ch));//数组10个字符10个字节 test1(arr);//问题1:这个是多少呢? test2(ch);//问题2:这个又是多少呢? return 0; }
问题1和问题2其实本质一样,这里把arr和ch传给函数test1和test2,函数传参,这里数组传过去的是首元素地址,也就是相当于指针,而sizeof计算地址大小这个就是由编译器是X64还是X32决定的了,如果是64位的那么大小应该是8,如果是32位的大小应该是4.
3 .前置++和后置++
前置++: 先++后使用。
后置++:先使用后++。
int main() { int a = 10; int x = ++a; //前置++,先将a自增变成11,再使用a,将a赋值给x,x=11; printf("a=%d x=%d\n", a, x); return 0;
int main() { int a = 10; int y = --a; //前置--,先将a自减成9,再使用a,将a赋值给x ,x=9 printf("a=%d y=%d\n", a, y); }
int main() { int a = 10; int z = a++; //后置++,先将a赋值给z,z=10,再给a自增+1,a就变成11; printf("a=%d z=%d\n", a, z); }
int main() { int a = 10; int v = a--; //后置--,先将a赋值给y,y=10,再给a自建-1,a就变成9了; printf("a=%d v=%d\n", a, v); }
6.关系操作符
> >= < <= != == 注意和一个=的区别
7.逻辑操作符
&& 逻辑与 || 逻辑或
区别:
逻辑与 -------按位与
逻辑或 -------按位或
1&&2 ---->1(表示真) 1&2 ---->0(二进制进行计算) 1||2 ---->1(表示真) 1|2 ---->3(二进制进行计算)
逻辑与和逻辑或有什么特点呢?
短路求值!
比如逻辑与&&: 表达式1&&表达式2 表达式1为假则右边不再进行。
逻辑或|| : 表达式1||表达式2 表达式1为真则右边不再进行。
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; } 因为 i = a++ && ++b && d++; a++是后置++,先使用后++,所以a=0,先使用与&&进行配对,然后是假, 所以后面++b, d++都不再进行,但a++,这个还是进行的,所以a用完后还要给a+1, 所以a=1,b=2,c=3,d=4;
/
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++(a=0,先使用与||配对为假,再+1,++b,b自增为3与||配对为真,后面的d++不再进行了所以 a=1,b=3,c=3,d=4;
8.条件操作符
表达式1 ? 表达式2 :表达式3 表达式1成立--->最终结果为表达式2,否则为表达式3.
一般条件表达式(if else )可以用条件操作符代替
求两个数中较大值:
int main() { int a = 10; int b = 20; int max = (a > b ? a : b);//a表达式1 a>b如果成立那么max=a,否则max = b; printf("%d", max); return 0; }
9.逗号表达式
表达式1,表达式2,……表达式n
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一 个表达式的结果
int main() { int a = 0; int b = 1; int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式 printf("%d", c); //c是多少? return 0; } 虽然结果是最后一个但是前面的逗号表达式还是要进行的,第一个a>b假的,不用管,第二个a=b+10,a=11了,第三个b=a+1,那b就等于12
a = b + 1; c = a / 2; if (d > 0) { …… } 写成逗号表达式if (a = b + 1, c = a / 2, d > 0)
在这里插入代码片
10.下标引用、函数调用和结构成员
1. [ ]下标引用操作符
操作数:一个数组名+一个索引值
int main() { int arr[5] = { 0,1,2,3,4};//创建数组,初始化 printf("%d",arr[4]);//arr[4]是等于4 arr[4]=10; //使用下标引用改变arr[4]成10 []操作数是arr和 5 return 0; }
2.( )函数调用操作符
接收一个或多个操作数,第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
void test() { printf("hello\n"); } void test1(char * ch) { printf("%s", ch); } int main() { test(); test1("hello"); return 0; }
3. . 访问一个结构的成员
. 结构体.成员名 -> 结构体指针->成员名
struct Stu { char name[10]; int age; char sex[5]; double score; }; void set_age1(struct Stu stu) { stu.age = 18;//结构体成员访问 .访问 } void set_age2(struct Stu* pStu) { pStu->age= 18;//结构体成员访问,指针访问。 } int main() { struct Stu stu; struct Stu* pStu = &stu;//结构体成员访问。 stu.age = 20;//结构体成员访问 set_age1(stu); pStu->age = 20;//结构体成员访问 set_age2(pStu); return 0; }
11.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转化成其他类型
11.1.隐式类型转化
C语言算术运算总是至少以缺省型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转化为普通整形,这种转化称为整形提升。
整形提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的 字节长度一般 就是int的字节长度, 同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为 CPU 内整型操作 数的标准长度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有 这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才 能送入CPU去执行运算
int main() { char a, b, c; a = b + c;//b+c要进行整形提升,b和c的值被提升到普通整形,然后再进行加法运算 //加法运算完成之后,结果将被截断,然后再存贮到a中。 return 0; }
怎么进行整形提升的呢?
整形提升是按照变量的数据类型的符号位来提升的
负数的整形提升
> int main() { char ch = -1;//字符一个字节---8个比特位 变量ch的二进制位(补码)中只有8个比特位; 11111111 因为char为有符号的char 所以整形提升的时候,高位补充符号位,即为1 提升之后的结果是: 11111111111111111111111111111111 return 0; }
正数整形提升
int main() { char ch = 1; //变量ch的二进制位(补码中)只有8个比特位 00000001; 因为char 为有符号的char 所以整形提升的时候,高位补充符号位即为0; 提升之后的结果就是: 00000000000000000000000000000001 无符号整形提升,高位补0; return 0; }
整形提升的例子1:
int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if (a == 0xb6) { printf("a"); } if (b == 0xb600) { printf("b"); } if (c = 0xb600000) { printf("c"); } return 0; }
结果是什么呢?
这里是因为a和b都要先进行整形提升,而c不需要进行整形提升。
a,b整形提升之后变成了负数,所以表达式,a==0xb6,b= =0xb600为假,c的结果为正,所以只打印c
实例2:
int main() { char a = 1; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(+a)); printf("%d\n", sizeof(-a)); return 0; }
嘿嘿结果又是多少呢?
神奇嘛?
实例2中的,a只要参与表达式运算的,就会发生整形提升,表达式+a,就会发生整形提升,所以sizeof(+a)就是4个字节。
表达式-a也会发生整形提升,所以sizeof(-a)也是4个字节,但sizeof(a)没有发生整形提升所以是1个字节。
11.2.算术转化
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转化为另一个操作数的类型,否则操作就无法进行,下面的层次体系为寻常算术转化
long double double float unsigned long int long int unsigned int int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转化为另一个操作数的类型后进行运算
但要注意算术转化要合理,要不然就会存在一些潜在问题
float a=3.14;
int num =a; 在会发生隐式转化,精度丢失
11.3.操作符的属性
复杂的表达式的求值会有三个影响因素:
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序。
两个相邻的操作符先执行哪一个?这个取决于他们的优先级,如果两个优先级相同,那再取决与他们的结合性。
操作符优先级:
下面给出详细的操作符的优先级表格,从上至下优先级依次递减(越靠近上面,操作符的优先级越高)
![在这