1. 操作符分类:
算术操作符
移位操作符
位操作符
赋值操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员
2. 算术操作符
+ - * / %
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
3. 移位操作符
<< 左移操作符
>> 右移操作符
注:移位操作符的操作数只能是整数。
移位操作符移动的是二进制的位。
整数的二进制表示形式有三种:原码,反码,补码。
原码:按照数值的正负,直接写出的二进制序列就是原码,一个整数4个字节,32个bit位,一个整数的二进制序列就是32个bie位。对于有符号的整数来说,最高位是符号位,0为正数,1为负数。对于无符号整数来说,没有符号位,所有位都是有效位。
反码:反码的符号位不变,其他位按位取反。
补码:反码的二进制+1得到补码。
对于正整数来说,原码,反码,补码相同,无需计算。
对于负整数来说,原码,反码,补码需要计算。
不管是正整数还是负整数,在内存中存储的都是补码的二进制序列。
整数在计算的时候使用的也是补码。
10 原码:00000000 00000000 00000000 00001010 反码:00000000 00000000 00000000 00001010 补码:00000000 00000000 00000000 00001010 -10 原码:10000000 00000000 00000000 00001010 反码:11111111 11111111 11111111 11110101 补码:11111111 11111111 11111111 11110110
3.1 左移操作符
移位规则:
左边抛弃、右边补0.
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { //左移操作:左边丢弃,右边补0 //00000000000000000000000000000111 int m = 7; int n = m << 1; //00000000000000000000000000001110 printf("%d\n", m); printf("%d\n", n); return 0; }
在左移后m的值还是7,m只是参与运算,<<操作符有*2的类似效果。
负数:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { //左移操作:左边丢弃,右边补0 //原码:10000000000000000000000000000111 //负数要计算补码 //反码:11111111111111111111111111111000 //补码:11111111111111111111111111111001 int m = -7; int n = m << 1; //左移后:11111111111111111111111111110010 //打印出来的是原码,所以我们计算左移后的原码,-1取反得到原码 //11111111111111111111111111110001 //10000000000000000000000000001110 printf("%d\n", m); printf("%d\n", n); return 0; }
左移操作符可以使得一些二进制数字来到我们想要的地方。
3.2 右移操作符
移位规则:
首先右移运算分两种:
1. 逻辑移位
左边用0填充,右边丢弃
2. 算术移位
左边用原该值的符号位填充,右边丢弃
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = -10; //原码:10000000 00000000 00000000 00001010 //反码:11111111 11111111 11111111 11110101 //补码:11111111 11111111 11111111 11110110 int b = a >> 1; //逻辑位移:01111111 11111111 11111111 11111011 //算数位移:11111111 11111111 11111111 11111011 //反码:11111111 11111111 11111111 11111010 //原码:10000000 00000000 00000000 00000101-> -5 printf("a=%d\n", a); printf("b=%d\n", b); return 0; }
使用逻辑位移还是算术位移取决的是编译器,大部分是算术右移,逻辑右移太简单粗暴。
右移有类似除2的效果。
警告⚠:
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
1. int num = 10; 2. num>>-1;//error
4. 位操作符
位操作符有:
1. & //按位与 2. | //按位或 3. ^ //按位异或 4. 注:他们的操作数必须是整数。
&运算,用补码进行运算,对应的二进制位有0则为0,两者为1才为1。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 3; int b = -5; int c = a & b;//按(2进制)位与 //00000000000000000000000000000011 --- 3的补码 //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 --- -5的补码 //00000000000000000000000000000011 --- 3的补码 //00000000000000000000000000000011 printf("%d\n", c); return 0; }
&的特点是得到某一个你想要的位。任何数字&1得到的他二进制位的最后一位。配合移位操作符将我们想要的二进制数字移位到最后一位,&1就可以得到这一位。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 3; //如果想要得到3的最低位 //a&1 //00000000000000000000000000000011 --- 3的补码 //00000000000000000000000000000001 ---1的补码 //遇到0则为0,3的最低位之前的所有位都会变成0,如果结果是0,表示3的最低位是0,如果结果是1,3的最低位就是1 return 0; }
| 运算:
用补码进行运算,对应的二进制位有1则为1,两者为0才为0。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 3; int b = -5; //00000000000000000000000000000011 --- 3的补码 //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 --- -5的补码 int c = a | b; //00000000000000000000000000000011 --- 3的补码 //11111111111111111111111111111011 --- -5的补码 //11111111111111111111111111111011 //11111111111111111111111111111010 //10000000000000000000000000000101 -5 printf("%d\n", c); return 0; }
^操作符:
相同为0,相异为1.\
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 3; int b = -5; //00000000000000000000000000000011 --- 3的补码 //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 --- -5的补码 int c = a ^ b; //00000000000000000000000000000011 //11111111111111111111111111111011 //11111111111111111111111111111000 //10000000000000000000000000000111 //10000000000000000000000000001000 // printf("%d\n", c); return 0; }
接下来我们看一道面试题:不创建临时变量,实现两个数的交换。
这个代码存在缺陷,变量的值如果太大就出问题了。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 3; int b = 5; printf("a=%d b=%d\n", a, b); a = a + b; b = a - b; a = a - b; printf("a=%d b=%d\n", a, b); return 0; }
这个代码使用^操作符来实现,但是这种方法的效率不是很高,创建临时变量是最好的。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 3; int b = 5; int c = 0;//中间变量 printf("a=%d b=%d\n", a, b); a = a ^ b;//a = 3^5 b = a ^ b;//b=3^5^5 b=3 a = a ^ b;//a= 3^5^3^5^5 a=5 printf("a=%d b=%d\n", a, b); return 0; }
5. 赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重 weight = 89;//不满意就赋值 double salary = 10000.0; salary = 20000.0;//使用赋值操作符赋值 赋值操作符可以连续使用,比如: int a = 10; int x = 0; int y = 20; a = x = y+1;//连续赋值 这样的代码感觉怎么样? 那同样的语义,你看看: x = y+1; a = x; 这样的写法是不是更加清晰爽朗而且易于调试
复合赋值符
1. += 2. -= 3. *= 4. /= 5. %= 6. >>= 7. <<= 8. &= 9. |= 10. ^=
这些运算符都可以写成复合的效果。
比如:
1. int x = 10; 2. x = x+10; 3. x += 10;//复合赋值 4. //其他运算符一样的道理。这样写更加简洁。
6. 单目操作符
6.1 单目操作符介绍
单目操作符就是只有一个操作数。
! 逻辑反操作 - 负值 + 正值 & 取地址 sizeof 操作数的类型长度(以字节为单位) ~ 对一个数的二进制按位取反 -- 前置、后置-- ++ 前置、后置++ * 间接访问操作符(解引用操作符) (类型) 强制类型转换
!可以把假变成真,把真变成假。
&是取地址操作符,用指针变量来存储。
*是解引用操作符,*x是通过x中存放的地址,知道x指向的对象。
sizeof其实我们之前已经见过了,可以求变量(类型)所占空间的大小。
前置++:计算口诀:先+1,后使用。
后置++:口诀:先使用,后+1。
--跟++是一样的。
6.2 sizeof 和 数组
数组传参是是首元素地址,是指针,地址的大小在32位平台是4个字节,64位平台是8个字节。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include <stdio.h> void test1(int arr[]) { printf("%d\n", sizeof(arr));//(2) } void test2(char ch[]) { printf("%d\n", sizeof(ch));//(4) } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//(1) printf("%d\n", sizeof(ch));//(3) test1(arr); test2(ch); return 0; }
7. 关系操作符
1. > 2. >= 3. < 4. <= 5. != 用于测试“不相等” 6. == 用于测试“相等
这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。
警告:
在编程的过程中== 和=不小心写错,导致的错误。
8. 逻辑操作符
1. && 逻辑与 2. || 逻辑或
&&都真才为真,||一个为真就是真。
360笔试题
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = a++ && ++b && d++; //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,所以这个表达式都为假,后面的不需要算了,所以只有a+1了,其他没变,i=0。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include <stdio.h> int main() { int i = 0, a = 0, b = 2, c = 3, d = 4; /*i = a++ && ++b && d++;*/ i = a++||++b||d++; printf(" a = %d\n b = %d\n c = %d\n d = %d\n i = %d\n", a, b, c, d,i); return 0; }
这里有一个数不为0则为真,所以都要运算,i=1.