1. 操作符分类:
思维导图:
2. 算术操作符
+ - * / %
#include int main() { int a = 10 / 3;//除法操作符,两边都是整数就执行整数除法 printf("%d\n", a); double b = 10.0 / 3;//两边只要有一个是浮点数就能计算出小数 printf("%lf\n", b); printf("%.1lf\n", b);//在lf前面加.1能保留小数点后一位,.几就保留几位 return 0; }
输出结果:
输出:
3
3.333333
3.3
#include int main() { int ret = 10 % 3;//取模,操作符两边必须是整数 printf("%d\n,ret"); return 0; }
输出结果:
输出:1
3. 移位操作符
在讲移位前,需要知道一些基本的概念:
二进制:
二进制整数有三种表示形式:
1. 原码
2. 反码
3. 补码
而在内存中存储的是:二进制的补码
所以参数在移位时是二进的补码。
例:
int main() { //整形类型占四个字节(32个比特位),二进制的表现形式: int a = 10; //00000000000000000000000000001010 - 原码 //00000000000000000000000000001010 - 反码 //00000000000000000000000000001010 - 补码 int a = -10; //10000000000000000000000000001010 - 原码 //11111111111111111111111111110101 - 反码 //11111111111111111111111111110110 - 补码 return 0; }
通过观察发现正数原码、反码、补码相同,
负数反码是:原码符号位不变,其它位按位取反,补码是:反码+1。
3.1 左移操作符
例:
#include int main() { //左移操作符:<< //规则:左边抛弃,右边补零 int a = 10; //a: //00000000000000000000000000001010 - 补码 int b = a << 1; //b: //00000000000000000000000000010100 - 补码 printf("%d\n", b); int c = -10; //c: //10000000000000000000000000001010 - 原码 //11111111111111111111111111110101 - 反码 //11111111111111111111111111110110 - 补码 int d = c << 1; //d: //11111111111111111111111111101100 - 补码 //11111111111111111111111111101011 - 反码 //10000000000000000000000000010100 - 原码 printf("%d\n", d); return 0; }
(注:printf 打印出来给我们看的是原码。)
输出结果:
输出:
20
-20
总结:
左移操作也可以看成是乘二的操作。
3.2 右移操作符
例:
#include int main() { //右移操作符:>> //1.算数右移:左边补符号位,右边抛弃(常用) //2.逻辑右移:左边补零,右边抛弃 int a = 10; //a: //00000000000000000000000000001010 - 补码 int b = a >> 1; //b: //00000000000000000000000000000101 - 补码 printf("%d\n", b); int c = -10; //c: //10000000000000000000000000001010 - 原码 //11111111111111111111111111110101 - 反码 //11111111111111111111111111110110 - 补码 int d = c >> 1; //d: //11111111111111111111111111111011 - 补码 //11111111111111111111111111111010 - 反码 //10000000000000000000000000000101 - 原码 printf("%d\n", d); return 0; }
输出结果:
输出:
5
-5
注:无论是右移还是左移的位数都不能为负数。
例:
ret>>-1
这样写是错误的
4. 位操作符
& 按位与
| 按位或
^ 按位异或
4.1 &
例:
#include int main() { //& - 按二进制与 //规则:有零则零 int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 - 原码 //11111111111111111111111111111010 - 反码 //11111111111111111111111111111011 - 补码 int c = a & b; //00000000000000000000000000000011 - a 补码 //11111111111111111111111111111011 - b 补码 //00000000000000000000000000000011 - c 原码 printf("%d\n", c); return 0; }
输出结果:
输出:3
4.2 |
例:
#include int main() { // | - 按二进制位或 // 规则:有一则一 int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 - 原码 //11111111111111111111111111111010 - 反码 //11111111111111111111111111111011 - 补码 int c = a | b; //00000000000000000000000000000011 - a补码 //11111111111111111111111111111011 - b补码 //11111111111111111111111111111011 - c补码 //11111111111111111111111111111010 - c反码 //10000000000000000000000000000101 - c原码 printf("%d\n", c); return 0; }
输出结果:
输出:-5
4.3 ^
例:
#include int main() { // ^ - 按二进制位异或 // 规则:相同为零,相异为一 int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 - 原码 //11111111111111111111111111111010 - 反码 //11111111111111111111111111111011 - 补码 int c = a ^ b; //00000000000000000000000000000011 - a补码 //11111111111111111111111111111011 - b补码 //11111111111111111111111111111000 - c补码 //11111111111111111111111111110111 - c反码 //10000000000000000000000000001000 - c原码 printf("%d\n", c); return 0; }
输出结果:
输出:-8
练习:
一道编程题:
如何不创建临时变量完成两个数的交换
例:
#include int main() { int a = 3; int b = 5; printf("%d %d\n", a, b); a = a ^ b; b = a ^ b;// b = a ^ b ^ b // 而 a ^ b ^ b = a 所以b就赋值为a a = a ^ b;// a = a ^ b ^ a // 而 a ^ b ^ a = b 所以a就赋值为b printf("%d %d\n", a, b); return 0; }
输出结果:
输出:
3 5
5 3
用异或操作符交换两个变量的弊端:
1. 可读性差
2. 效率没有创建临时变量高
3. 异或只能用于整数变量的交换
总结:这种方法了解即可,平时使用临时变量交换两个变量的方法更好。
5. 赋值操作符
赋值操作符能给变量赋值。
例:
int main() { int a = 10; a = 100;// = 能将a赋值成100 return 0; }
还有复合赋值符:
例:
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果(规则都是一样的)
例:
int main() { int a = 10; a += 10;//这个其实就是:a = a + 10 printf("%d\n", a);//输出的结果就是20 return 0; }
6. 单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
+ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
6.1 单目操作符介绍
例1:
#include int main() { //! 逻辑反操作 //C语言中0表示假,非零表示真 int n = 0; if (n) { printf("1\n"); } if (!n)//!逻辑反操作,将假变为真(也能从真变假) { printf("2\n"); } return 0; }
输出结果:
输出:2
例2:
#include int main() { // + - int a = -10; printf("%d\n", a); printf("%d\n", -a); printf("%d\n", +a);//‘+’几乎没有用处 return 0; }
输出结果:
输出:
-10
10
-10
例3:
#include int main() { // ~ 按位取反 //00000000000000000000000000000000 //11111111111111111111111111111111 - 补码是全1 int a = 0; printf("%d\n", ~a);
}
输出结果:
输出:-1
例4:
#include int main() { //++ int a = 10; int b = a++;//后置++,先使用,再++ printf("%d\n", b); printf("%d\n", a); int c = 10; int d = ++c;//前置++,先++,再使用 printf("%d\n", d); printf("%d\n", c); return 0; }
输出结果:
输出:
10
11
11
11
注:-- 的规则与 ++ 是一样的。
#include int main() { // ++ -- 是带有副作用的 // 会改变变量自身的值 //1 int a = 10; int b = ++a;//b=11 a=11 //2 int a = 10; int b = a + 1;//b=11 a=10 return 0; }
例5:
#include int main() { // (类型)强制类型转换 int a = (int)3.14;//这样编译器就不会报警告 srand((unsigned int)time(NULL));//将类型为time_t的time转成srand需要的无符号整形 return 0; }
6.2 sizeof 和数组
例:
#include void test1(int arr[])//本质上传过来的是数组首元素的地址 { printf("%d\n", sizeof(arr));//地址在32位环境占4个字节 } //在64位的环境中占8个字节 void test2(char ch[]) { printf("%d\n", sizeof(ch));//我是32位的环境,所以输出4 } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//int类型占4个字节 printf("%d\n", sizeof(ch));//char类型占1个字节 test1(arr); test2(ch); return 0; }
输出结果:
输出:
40
10
4
4
7. 关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
注:别把 “==” 和 “=” 给写错了。
8. 逻辑操作符
&& 逻辑与 ( a&&b a,b都要满足才真)
|| 逻辑或 ( a||b a,b只要满足一个就真)
例:
输出结果:
输出:
a = 1
b = 2
c = 3
d = 4
#include int main() { int i = 0, a = 1, b = 2, c = 3, d = 4; i = a++ || ++b || d++; //(a++)这个表达式为真,那||左边就无须计算了 //所以(++b)(d++)都不计算了 printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d); return 0; }
输出结果:
输出:
a = 2
b = 2
c = 3
d = 4
9. 条件操作符
exp1 ? exp2 : exp3
例:
这是一个求较大值的代码:
#include int max(int a, int b) { if (a > b) return a; else return b; } int main() { int a = 10; int b = 20; printf("%d\n", max(a, b)); return 0; }
输出结果:
输出:20
而用条件操作符:
#include int max(int a, int b) { return (a > b ? a : b);//如果a>b则取a,否则取b } int main() { int a = 10; int b = 20; printf("%d\n", max(a, b)); return 0; }
输出结果:
输出:20
两种写法是一模一样的。
10. 逗号表达式
逗号表达式:
1. 用逗号隔开的多个表达式。
2. 从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
例:
#include int main() { int a = 1; int b = 2; int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式 //先算a = b + 10,算出a = 12,再算b = a + 1,算出b = 13, //最后结果就是最后一个表达式(b = a + 1)的值 printf("c = %d\n", c); return 0; }
输出结果:
输出:13
11. 结构成员
结构体在C语言中一般用于描述复杂对象,比如人、书,C语言中没有这样的类型,
但是结构体能让我们创建新的类型。
12. 表达式求值
12.1 隐式类型转换
CPU在进行运算的时候一般使用整形int,所以在有些时候,当一个小于整形的类型进行计算时,计算机就会先进行整形提升再进行运算,这就是隐式类型转换。
(通用CPU是难以直接实现两个非整形的直接相加运算)
例:
// char short int long ... // 1 2 4 int main() { //char --> signed char char a = 3; //截断 //00000000000000000000000000000011 //00000011 - a // char b = 127; //00000000000000000000000001111111 //01111111 - b char c = a + b; //00000011 //01111111 //整型提升 //00000000000000000000000000000011 - a //00000000000000000000000001111111 - b //00000000000000000000000010000010 - a + b =c //截断 //10000010 - c printf("%d\n", c); //%d 是打印十进制的整数 //11111111111111111111111110000010 - 补码 //11111111111111111111111110000001 - 反码 //10000000000000000000000001111110 - 原码 //-126 return 0; }
输出结果:
输出:-126
注:
char:
有符号的char的取值范围是:-128~127
无符号的char的取值范围是:0~255
12.2 算术转换
不同操作类型的数进行运算时会进行算数转换:
long double
double
float unsigned long int
long int
unsigned int
int
注:算数转换不合理会产生一些问题:
#include int main() { float f = 3.14; int num = f;//隐式转换,会有精度丢失 printf("%d\n", num); return 0; }
输出结果:
输出:3
12.3 操作符的属性
复杂表达式的求值有三个影响的因素:
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序
但是,我们写出的代码如果没有唯一的运算路径,就会出问题
所以,总结:平时写代码的运算路径要唯一。
写在最后
以上就是本篇文章的内容了,感谢你的阅读。
如果喜欢本文的话,欢迎点赞和评论,写下你的见解。
如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。