目录
前言
首先我建议在学习使用操作符的过程中,不要死记操作符的优先级,通常我们只需要记住乘除的优先级大于加减之类简单的即可,在实际使用中,我们尽量多使用 ' () ' 来改变操作符的优先级,这样做既可以避免去背繁琐的操作符优先级,防止记错误用,还可以提高代码的可读性,对表达式的执行顺序一目了然
操作符的学习需要结合具体实例,作者本人在学完操作符之后,在刷题的过程中发现很多操作符自己根本不会用(在解题过程中没有想到,就是常见的理解了概念,但是拿不出手),看完大佬解题巧妙使用操作符之后总是感到惊奇,本文我会尽力给出一些例题辅助学习。(例题来自课本或者常见的刷题网站)
操作符分类
1.算术操作符
2.移位操作符
3.位操作符
4.赋值操作符
5.单目操作符
6.关系操作符
7.逻辑操作符
8.条件操作符
9.逗号表达式
10.函数调用、下标引用和结构体成员
C语言的操作符大概可以分为以上类别,接下来我们依次讲解
1.算术操作符
算术操作符
+ - * / %+ - 加减操作符等价于数学的加减
/ 除法操作符
除法操作符的两个操作数都是整数的话,执行的是整型除法
除法操作符的两个操作数只要有一个为小数,执行的是浮点型除法现有n盒未打开的酸奶,假设小猴每h分钟能够喝完一盒酸奶, 并且它喝光一盒酸奶之前不会喝另一盒,那么经过m分钟后还有多少盒未打开的酸奶? #include<stdio.h> int main() { float n = 0, h = 0, m = 0; scanf("%f%f%f", &n, &h, &m); int boxes = (n-(m/h)); printf("%d", boxes); return 0; }
% 取模操作符
取模操作符的两个操作数只能是整型其它算术操作符的两个操作数既可以是整型,也可以是浮点型
判断整数的奇偶性 int main() { int i = 0; scanf("%d", &i); if(i%2 == 0) printf("Even\n"); else printf("Odd\n"); return 0; }
2.移位操作符
<< >>
移位操作符,“位”指的是二进制位,如果对二进制不够清楚,可以参考后面的补充-原码反码补码
<< 左移操作符
规则:左边丢弃,右边补零
我们以32位状态下为例:
实际上,num的值并没有发生改变
>> 右移操作符
右移操作符分为两种,它们的移位规则并不相同:
1.逻辑移位:
左边补零,右边丢弃
2.算术移位
左边的符号位不变,右边丢弃
我们可以看到当前编译器VS2022的右移操作符执行的是算术右移
实际上,执行逻辑移位还是算术移位是由编译器自己决定的
Warning:对于移位运算符,不要移动负数位
计算2的n次方 #include <stdio.h> int main() { int n = 0; scanf("%d", &n); int sum = 1; for (int i = 0; i < n; i++) { sum <<= 1; } printf("%d", sum); return 0; }
3.位操作符
& | ^
它们的操作数必须是整数,“位”指的是二进制位,如果对二进制不够清楚,可以参考后面的补充-原码反码补码
& 按位与
规则:比较两个操作数二进制相同位置的数码,有0则0,全1为1
int a = 3;//0011 int b = 5;//0101 int c = a & b; //c = 1 0001 统计一个整数的二进制形式数码1的个数 #include <stdio.h> int main() { int n = 0; int k = 0; scanf("%d",&n); while(n) { n &= (n-1); k++; } printf("%d", k); return 0; }
| 按位或
规则:比较两个操作数二进制相同位置的数码,有1则1,全0为0
int a = 3;//0011 int b = 5;//0101 int c = a | b; //c = 7 0111
^ 按位异或
规则:比较两个操作数二进制相同位置的数码,相异为1,相同为0
int a = 3;//0011 int b = 5;//0101 int c = a ^ b; //c = 6 0110 不创建临时变量交换两个数的值 #include <stdio.h> int main() { int a = 3; int b = 4; //下面这种方法虽然也可以,但是a+b如果太大可以会丢失数据 //int a = a + b - a; //int b = a + b - b; a = a ^ b; b = a ^ b; a = a ^ b; printf("%d %d\n", a, b); return 0; }
4.赋值操作符
=
赋值操作符可以给一个可修改的左值重新赋值
赋值操作符可以连续赋值
复合赋值符
+= -= *= /= %= <<= >>= &= |= ^=
5.单目操作符
! 逻辑反操作 sizeof 计算操作数的类型长度(单位:字节)
- 负值 -- 位于操作数前面或后面作用不同
+ 正值 ++ 位于操作数前面或后面作用不同
& 取地址 ~ 对一个二进制按位取反
* 间接访问操作符 (类型) 强制类型转换
! 逻辑反操作
判断整数的奇偶性还可以这样写 int main() { int i = 0; scanf("%d", &i); if (!(i % 2 == 0)) // <=> i % 2 == 0 不成立 printf("奇数\n"); else printf("偶数\n"); return 0; }
~ 对一个二进制按位取反
判断输入字符的类型 #include <stdio.h> int main() { char ch = 0; while (~scanf("%c", &ch)) // <=> while(scanf("%c", &ch) != EOF) { if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) printf("字母\n"); else if (ch >= '0' && ch <= '9') printf("数字\n"); else printf("其他\n"); getchar(); } return 0; } 注意: EOF的值为 -1, 二进制形式各位均为1, 按位取反后各位上均为0, 最终结果为假, 从而结束循环
++ -- 自增、自减
我们在循环语句中已经对它们不陌生了
顺序/逆序打印一个数组 #include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } //逆序打印 /*for (int i = 9; i >= 0; i--) { printf("%d ", arr[i]); }*/ return 0; }
(类型) 强制类型转换
现有n盒未打开的酸奶,假设小猴每h分钟能够喝完一盒酸奶, 并且它喝光一盒酸奶之前不会喝另一盒,那么经过m分钟后还有多少盒未打开的酸奶? #include<stdio.h> int main() { float n = 0, h = 0, m = 0; scanf("%f%f%f", &n, &h, &m); printf("%d", (int)(n-(m/h)); // float -> int 类型 return 0; } 字符转ASCII码 #include<stdio.h> int main() { char i = 0; scanf("%c",&i); printf("%d\n",i); // char -> int return 0; }
* 取地址、& 解引用(注意区别 逻辑与)
1 #include <stdio.h> 2 int main() 3 { 4 int num = 10; 5 int* p = # 6 printf("%d", *p); 7 return 0; 8 } 注意:第5、6行,* 的作用不同
sizeof()
6.关系操作符
< <= > >= != ==
!= 用于测试 不相等
== 用于测试 相等
关系操作符常用于if语句
注意不要把 " == " 写成 " = "
7.逻辑操作符
&& 逻辑与 、 || 逻辑或
用法:表达式1 操作符 表达式2
&& 逻辑与
表达式1和表达式2同时成立,返回值为真,否则为假
|| 逻辑或
表达式1和表达式2其中之一成立,返回值为真;表达式1和表达式2都不成立,返回值为假
判断下列程序的运行结果 int main() { int i = 0; int a = 0, b = 2, c = 3, d = 0; i = a++ && ++b && d++; //i = a++ || ++b || d++; printf("%d %d %d %d\n", a, b, c, d); return 0; }
实际上,对于 && 如果表达式1为假,就可以判断(表达式1 && 表达式2)的结果为假了,此时计算机不再运行表达式2;对于 || 如果表达式1为真,就可以判断(表达式1 && 表达式2)的结果为真了,此时计算机不再运行表达式2
判断是否为闰年 int main() { int n = 0; scanf("%d", &n); if (n % 400 == 0 || (n % 4 == 0 && n % 100 != 0)) printf("yes"); else printf("no"); return 0; }
8.条件操作符
表达式1 ? 表达式2 :表达式3
有些场合条件操作符甚至可以代替if语句
输出两个数的较大值 #include <stdio.h> int main() { int a = 0, b = 0; int max = 0; scanf("%d %d", &a, &b); if (a >= b) max = a; else max = b; printf("%d\n", max); return 0; } 等效代码 int main() { int a = 0, b = 0; int max = 0; scanf("%d %d", &a, &b); max = a > b ? a : b; printf("%d\n", max); return 0; }
9.逗号表达式
表达式1, 表达式2, 表达式3, ..., 表达式N;
10.函数调用、下标引用和结构体成员
[] 下标引用操作符
我们在引用数组的元素的时候最常使用的就是下标,数组名 + [下标] 可以非常便捷地使用数组的元素,[]的两个操作数是 数组名 和 下标
输入一个整型序列,判断它是否有序 int main() { int i,arr[100],n,cnt1=0,cnt2=0; scanf("%d",&n); for(i=0;i<n;i++) { scanf("%d",&arr[i]); } for(i=0;i<n-1;i++) { if(arr[i]-arr[i+1]>=0) cnt1++;//降序计数 else if(arr[i]-arr[i+1]<=0) cnt2++;//升序计数 } if(cnt1==n-1||cnt2==n-1)//一共n个数,两两相减的到n-1个结果 printf("sorted"); else printf("unsorted"); }
() 函数调用操作符
可以接受一个或多个操作数,第一个操作数的函数名,其它操作数是传递给函数的参数
#include <stdio.h> #include <string.h> void main() { char str1[] = "hello world"; char str2[20] = { 0 }; strcpy(str2, str1); printf("%s\n", str2); } main() strcpy() printf()
.、-> 结构体成员
访问一个一个结构体的成员,可以使用下面两种形式
结构体 . 成员名
结构体指针 -> 成员名
struct S { char ch; int id; }; void point(struct S* ps) { printf("%c\n%d", ps->ch, ps->id); } int main() { struct S s = { 'a',1234 }; printf("%c\n%d\n", s.ch, s.id); point(&s); return 0; }