1. 算术运算符
运算符 | 功能说明 | 举例 |
---|---|---|
+ | 加法,一目取正 | a+b |
- | 减法,一目取负 | a-b |
* | 乘法 | a*b |
/ | 除法 | a/b |
% | 取模(求余) | a%b |
++ | 自加1 | a++, ++b |
– | 自减1 | a–, --b |
- % 是算术运算符的重点,一般将某个数值控制在一定范围内,还有数据解析上
// demo1:产生一个随机数,并将此数控制在5的范围内
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char const *argv[])
{
// 产生随机因子
srand(time(NULL));
// 产生随机数,控制在5以内
int ret = rand() % 5;
printf("%d\n",ret);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
// 技巧1 :将一个数控制在指定范围内
int data = 6541235;
data = data % 10;
printf("%d\n",data);
// 技巧1 demo
// 通过时间产生一个随机因子
srand(time(0));
int ret = rand();
ret = ret % 5;
printf("%d\n",ret);
// 技巧2 demo, 取个位十位百位
int num = 123;
int num1 = num % 10;
printf("%d\n",num1);
int num2 = (num / 10) % 10;
printf("%d\n",num2);
int num3 = num / 100;
printf("%d\n",num3);
return 0;
}
-
关注点
- 减号也是负号,比如-a是取变量a的相反数
int a = 10; printf("%d\n",-a); // -10
- 取模运算要求左右两边操作数必须是整型数据
1.5 % 0.5 (错误)
- 自加自减运算不仅可以对整型操作,也可以对浮点数、指针操作
1)单目运算符 目:目标操作数
++ – +(正号) -(负号)
- 前后缀运算:
- 前缀自加自减运算:先进行自加自减,再参与表达式运算
- 后缀自加自减运算:先参与表达式运算,在进行自加自减
int a = 100;
int b = 200;
int ret = a + b;
int c = ++a; // a先自加1,变成101;然后再赋值给c,因此c等于101
int d = b++; // b先赋值给d,因此d等于200,然后b自加1,变成201
int a = 3;
int x = a+++a++;(错误的)
int b = 3;
int y = (b++)+(++b);(错误的)
error: multiple unsequenced modifications to [-Werror,-Wunsequenced]//多次非顺序修改
总结就是不要将自增或自减运算符与赋值运算符等其他运算符复合使用。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10;
// 单目运算符, a先赋值给b 然后a自己加一
int b = a++;
printf("%d,%d\n",a,b);
// 单目运算符, a1先自己加一 然后a1赋值给b1
int a1 = 10;
int b1 = ++a1;
printf("%d,%d\n",a1,b1);
b1 = --a1;
printf("%d,%d\n",a1,b1);
b1 = a1--;
printf("第%d行结果为:%d,%d\n",__LINE__,a1,b1);// 9 10
// 不规范,单目运算符不能多级运算
int c = (a1++) + (++a1);
printf("%d\n",c);
return 0;
}
2)双目运算符
+ - * /
printf("%d\n",3/2);// 一个整数除以另一个整数,小数被舍弃
printf("%f\n",3*1.0/2);// 如果想要小数,那么可以*1.0,隐式类型转换 1.500000
printf("%d\n",10%3);//%取余运算符的左右两边必须都是整数
// 一般取余运算符在我们编程开发中用于控制数据的大小范围
srand(time(NULL));// 获取随机数因子
int a9 = rand()%5;// rand()得到一个随机数0-4
printf("%d\n",a9);
2. 关系运算符
运算符 | 功能 | 举例 | 说明 |
---|---|---|---|
> | 大于 | a > b | 判断a是否大于b |
>= | 大于或等于 | a >= 5 | 判断a是否大于或等于5 |
< | 小于 | 3 < x | 判断3是否小于x |
<= | 小于或等于 | x <= (y+1) | 判断x是否小于或等于y+1 |
== | 等于 | (x+1) == 0 | 判断x+1是否等于0 |
!= | 不等于 | c != ‘\0’ | 判断c是否不等于’\0’ |
注意关系运算符的值为布尔值 也就是说要么关系成立(1) 要么不成立(0)
1 > 10 20 < 30
int a = 10; int b = 20;
a != b;// 1
a == b; // 0
demo:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10, b = 5;
printf("%d\n",a > b);// 10大于5,为真结果为1
printf("%d\n",a >= b);// 10大于或者等于5,为真,结果为1
printf("%d\n",a < b);// 10小于5,为假,结果0
printf("%d\n",a <= b);// 10小于5或者等于5,为假,结果0
printf("%d\n",a == b);// 10等于5,为假,结果0
printf("%d\n",a != b);// 10不等于5,为真,结果1
return 0;
}
- 关注点:
- 关系运算符用于判断运算符两边的表达式是否满足给定的大小条件
- 由关系运算符组成的表达式称为关系表达式,其值为布尔型
- 判断是否相等是双等号==,而不是一个等号
3. 逻辑运算符
运算符 | 功能说明 | 举例 |
---|---|---|
! | 逻辑反 | !(x==0) |
&& | 逻辑与 | x > 0 && x < 10 |
|| | 逻辑或 | y < 10 || x > 10 |
-
运算规则:
-
注意:c语言不能这么写 a < b < c这是数学的写法,应该要结合逻辑运算符一起编写
-
逻辑反:将逻辑真、假翻转,即真变假,假变真
-
逻辑与:将两个关系表达式串联起来,当且仅当左右两个表达式都为真时,结果为真。
-
逻辑或:将两个关系表达式并联起来,当且仅当左右两个表达式都为假时,结果为假。
-
-
特殊规则
- 在逻辑与运算中,如果左边表达式的值为假,那么右边表达式将不被执行。
- 在逻辑或运算中,如果左边表达式的值为真,那么右边表达式将不被执行。
逻辑与 && ---而且,同时 两个操作数都必须为真,结果为真 逻辑或 || ---或者 只要有一个为真,那么结果为真 逻辑非 ! ----反 非0 为真,0 为假 逻辑表达式结果有两种 1为真 0为假 int a = 10; int b = 20; //c语言 惰性运算,也就是说 如果前面的结果为假,后面就不会运行--知难而退 if(a > b && ++a) { printf("111\n"); } printf("%d\n",a);
demo2: #include <stdio.h> #include <stdbool.h> int main(int argc, char const *argv[]) { int a = 10, b = 5, c = 6, d = 7; // 10大于5并且6小于7为真,结果为1 printf("%d\n",a > b && c < d); // 10小于5或者6小于7为真,结果为1 printf("%d\n",a < b || c < d); // -1为真取反为假,结果为0 printf("%d\n",!-1); // c语言为惰性语言,只要表达式1能确定整条语句结果,则表达式2不执行 printf("%d,%d\n",a < b && a++,a); return 0; }
4. 位运算符(重点,对整个变量的某一位操作)
运算符 | 名称 | 举例 | 功能说明 |
---|---|---|---|
~ | 位逻辑反 | ~a | 将变量 a 中的每一位取反 |
& | 位逻辑与 | a & b | 将变量 a 和 b 逐位进行与操作 |
| | 位逻辑或 | a | b | 将变量 a 和 b 逐位进行或操作 |
^ | 位逻辑异或 | a ^ b | 将变量 a 和 b 逐位进行异或操作 |
<< | 左移 | a << 4 | 将变量 a 中的每一位向左移动4位 |
>> | 右移 | x >> n | 将变量 x 中的每一位向右移动4位 |
- ~ : 按位取反—单目运算符,数据将每一个bit位取反,1变0,0变1
unsigned char ret = ~0x05;// 0000 0101--> 1111 1010
//%u无符号输出,直接以补码的方式输出,%d以补码还原的方式输出
printf("%hhu\n",ret);// 250
printf("%hhd\n",ret);// -6
0x05 ----> 0000 0101
~0x05----> 1111 1010 ---》250
%d是以补码还原的方式进行输出
补码: 1111 1010
反码: 1111 1001
原码: 1000 0110--》-6
#include <stdio.h>
int main(int argc, char const *argv[])
{
char a = 0x03;
printf("%d\n",~a);
return 0;
}
图解:
-
&:按位与,两个都为1,结果才为1
- 1 & 1 == 1
- 1 & 0 == 0
- 0 & 0 == 0
- 0 & 1 == 0
unsigned char ret = 0x12; unsigned char ret1 = ret & 0x34; printf("%hhu\n",ret1);
-
| :按位或,两个都为1,结果才为1
- 1 | 1 == 1
- 1 | 0 == 1
- 0 | 1 == 1
- 0 | 0 == 0
-
^ : 按位异或 — 不同为1,相同为0
- 1 ^ 0 == 1
- 0 ^ 1 == 1
- 0 ^ 0 == 0
- 1 ^ 1 == 0
/********异或操作***********/ unsigned char a1 = 0x3c; unsigned char b1 = 0x47; printf("%d\n",a1 ^ b1);
-
<< : 左移,按bit位往左移动
- 1.无符号左移:
unsigned int a = 3 << 3; printf("%d\n",a);// 24
-
2.有符号左移
int a = -3 << 3; printf("%d\n",a);
-
>> : 右移
- 无符号右移
unsigned char a = 151 >> 5; printf("%d\n",a);//4
- 有符号右移
char a = -96 >> 5;
printf("%d\n",a);
注意:
1.在进行移位运算的时候,凡是被移出去的位都丢弃,凡是空出来的都补零,移位运算是针对的是无符号整数。
2. 如果非要进行有符号的移位运算,那么左移的时候,空出来的补0,右移的时候,空出来不符号位(原码阶段);
-
位运算符操作的对象是数据中的每一位
-
运算规则:
-
位逻辑反、位逻辑与、位逻辑或拥有与逻辑运算相似的规则和一样的真值表。
-
异或运算:相同为0,不同为1
-
移位运算:移出去的不要,空出来的补零。移位运算都是针对无符号数的运算。
左移运算
右移运算
结论: 无符号整数左移,并且有效数据1不丢失时,假设 data << n,等于 data * 2^n 无符号整数右移,并且有效数据1不丢失时,假设 data >> n,等于 data / 2^n
**问题1:** 假设有如下程序,程序输出的结果是什么? int main(void) { unsigned char a, b, c; a = 0x3; // 0000 0011 b = a|0x8; // 0000 0011 | 0000 1000 == 0000 1011 c = b<<1; // 0000 1011 << 1 == 0001 0110 printf("%d%d\n", b, c);// b:11 c:22 }
-
题目1:
假设有一个无符号32位整型数据
unsigned int data=0x12ff0045
请编写一个程序用位运算把data的第14、15位修改1,把22、23位修改为0, 并且输出修改后的数据。
unsigned int data = 0x12ff0045;
//data = data | 0x03 << 14;
data |= 0x03 << 14;
printf("%x\n",data);
//data = data ^ 0x03 << 22;
//data = data & ~(0x03<<22);
data &= ~(0x03<<22);
printf("%x\n",data);
5. 特殊运算符
- 赋值运算符
- 不能对常量赋值
- 只能对变量赋值
- 不能对数组赋值
- 可以连续赋值,从右往左
- 赋值运算符的左边(左操作数)必须是可写的地址空间
int a, b;
int x[5];
a = 100; // 对变量 a 赋值,正确
3 = 100; // 对常量 3 赋值,错误!
x = 123; // 对数组 x 赋值,错误!
// 连续赋值
a = b = 50; // 先将 50 赋给 b,再将 b 的值赋给 a,正确
- 复合赋值符
- 当左右两边有相同的操作数时,采用复合赋值符不仅直观,且能提高运算效率
- 除了下述10个复合运算符之外,生造别的复合运算符是非法的
// 加减乘除:
a += n; // 等价于 a = a+n;
a -= n; // 等价于 a = a-n;
a *= n; // 等价于 a = a*n;
a /= n; // 等价于 a = a/n;
// 求余:
a %= n; // 等价于 a = a%n;
// 位运算:
a &= n; // 等价于 a = a&n;
a |= n; // 等价于 a = a|n;
a ^= n; // 等价于 a = a^n;
a >>= n; // 等价于 a = a>>n;
a <<= n; // 等价于 a = a<<n;
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10;
a += 10; // 相当于 a = a+10;
a -= 10; // a = a-10;
a *= 10; // a = a * 10;
a /= 10; // a = a / 10;
a %= 10; // a = a % 10;
printf("%d\n",a);
return 0;
}
6. 条件运算符(三目运算符,重点)
- 唯一需要三个操作数的运算符
- 语法:表达式1?表达式2:表达式3
- 释义:当表达式1为真时,去表达式2,否则取表达式3
- 举例:
int a = 100;
int b = 200;
int m = (a>b) ? a : b; // 如果 a>b 为真,则 m 取 a 的值,否则取 b 的值
// 三目运算符
int a1 = 10;
int b1 = 20;
// 判断a1 > b1吗?如果大于则输出a1,否则输出b1
int ret = (a1 > b1) ? a1 : b1;c
printf("%d\n",ret);
7. sizeof 运算符
- 含义:计算指定数据类型或者变量所占据内存的字节数
- 语法:sizeof(类型)、sizeof(变量),计算变量的字节数时圆括号可以省略
- 举例:
printf("%d\n", sizeof(int));// 4
printf("%d\n", sizeof(long double));//12
int a[5];
printf("%d\n", sizeof(a));//4*5=20
printf("%d\n", sizeof a);
8. return运算符
- 含义:退出某个函数(如果退出的是主函数main,那么整个程序也就退出)
- 语法:必须出现在函数体内,可以带函数对应类型的数据
- 举例:
int main()
{
return 0;
}
9.逗号表达式:
(表达式1,表达式2,表达式3,... 表达式n)
求值顺序:
先求表达式1,再求表达式2,再求表达式3,最后求表达式n
整个逗号表达式的值为n的值
注意:
1.都好表达式的优先级最低
2.运算顺序是从左往右
3.整个都好表达式的值取决于最右边的表达式的值
int ret = (a>b,a++,b++,a);
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10, b = 20;
int ret = (a>b,a++,b++,a);
printf("%d\n",ret);
return 0;
}
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10;
int b = 20;
// 表达式1 :a > b = 0
// 表达式2 :a = 20
// 表达式3 :b < a = 0
// 表达式3 :a = 20;
// 总结:逗号运算符从左往右连续运算,取最右的表达式进行返回
int ret = (a>b,a=20,b<a,a);
printf("%d\n",ret);
return 0;
}
10. 优先级与结合性
- 当表达式出现不同的运算符时,根据优先级来决定谁先执行,比如先乘除后加减
- 当表达式中出现多个相同优先级的运算符时,更具结合性决定谁先运行,比如从左到右
总结:优先级熟悉就行,如果不确定哪个优先级高则可以在表达式中添加()