=========================================================================
相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)
=========================================================================
接上期:
学C的第十四天【应用多文件的形式实现 扫雷 程序(重点)、练习】-CSDN博客
=========================================================================
1. 操作符分类:
- 算术操作符: + - * / %
- 移位操作符: << >>
- 位操作符: & | ^
- 赋值操作符: = += -= *= /= ……
- 单目操作符: ! sizeof + - ~ & *
- 关系操作符: > < >= <= == !=
- 逻辑操作符: && ||
- 条件操作符: ? :
- 逗号操作符: ,
- 下标引用、函数调用和结构成员: [] () . ->
2. 算术操作符
(注:除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数)
+ : 加法运算
- : 减法运算
* : 乘法运算
/ : 除法运算,除数不能为0
分为:
1. 整数除法(除号两端都是整数)
2. 浮点数除法(除号两端只要有一个小数就执行浮点数除法)
% : 取模操作符,得到的是整除后的余数
1. 得到的是整除后的余数
2. %取模操作符的两个操作数必须是整数
3. 移位操作符
注:
移位操作符的操作数只能是整数,对于一位运算符,不要移动负数位,
这是标准未定义的,计算结果将取决于编译器,结果不可预料
补充:整数的二进制表示形式
有三种表现形式:
原码 | 反码 | 补码
1. 正整数的原码、反码、补码是相同的
2. 负整数的原码、反码、补码是要计算的
3. 不管是正整数还是负整数都可以写出二进制原码
4. 根据正负直接写出的二进制序列就是原码
5.移位移动的是二进制信息
6.整数在内存中存储的是补码
7.计算的时候也是使用补码计算的
(演示代码:)
#include <stdio.h> //不管是正整数还是负整数都可以写出二进制原码 //根据正负直接写出的二进制序列就是原码 int main() { //1个整型是4个字节 = 32个bit位 //符号位是 1 表示负数 //符号位是 0 表示正数 int a = 15; //正整数的原码、反码、补码是相同的 //00000000000000000000000000001111 -- 原码 //00000000000000000000000000001111 -- 反码 //00000000000000000000000000001111 -- 补码 int b = -15; //负整数的原码、反码、补码是要计算的 //10000000000000000000000000001111 -- 原码 //11111111111111111111111111110000 -- 反码(原码的符号位不变,其它位按位取反得到的就是反码) //11111111111111111111111111110001 -- 补码(反码 +1 就是补码) return 0; }
<< : 左移操作符 -- 补码 左边丢弃,右边补0
//左移操作符:补码 左边丢弃,右边补0 #include <stdio.h> int main() { //正整数: int a = 6; // 00000000000000000000000000000110 -- 正整数的 原码、反码、补码 相同 // 00000000000000000000000000000110 -- 补码 (移位操作的是 补码) //左移后: // 0 0000000000000000000000000000110 0 -- 补码 (原码 、 反码) // 因为正整数的 原码、反码、补码 相同,所以这也是原码 int b = a << 1; printf("%d\n", b); printf("%d\n", a); //负整数: int c = -6; // 10000000000000000000000000000110 -- 原码 // 11111111111111111111111111111001 -- 反码 (原码按位取反) // 11111111111111111111111111111010 -- 补码 (反码 +1 ) int d = c << 1; //左移后: // 1 1111111111111111111111111111010 0 -- 补码 // 11111111111111111111111111110011 -- 反码 (补码 -1) // 10000000000000000000000000001100 -- 原码 (符号位不变,反码按位取反) // -12 printf("%d\n", d); printf("%d\n", c); a = a << 1; // 也可以写成: a <<= 1; //符合符号 <<= :左移等于 ,类似 a += 1 return 0; }
>> : 右移操作符
分为:
1. 算术 右移 (补码 右边 丢弃, 左边 补原来的符号位)
2.逻辑 右移 (补码 右边 丢弃, 左边 直接补0)
(C语言没有明确规定到底是算术右移还是逻辑右移,一般编译器上采用的是算术右移)
#include <stdio.h> int main() { //正整数: int a = 15; //00000000000000000000000000001111 -- 补码 int b = a >> 1; // 0 0000000000000000000000000000111 1 -- 补码(移位后) //C语言没有明确规定到底是算术右移还是逻辑右移,一般编译器上采用的是算术右移 printf("%d\n", b); //算术右移 // b 得到的是 a 移位后的结果 // 算术右移(右边丢弃,左边补原来的符号位) // 从 二进制的1111 变成 二进制的111 ,从 十进制的 15 变成了 十进制的 7 printf("%d\n", a); // a >> 1,进行移位后,a没有发生变化, // 表达式不会影响 a 的 值 //负整数: int c = -15; //11111111111111111111111111110001 -- 补码 (符号位是 1 表示负数) int d = c >> 1; // 1 1111111111111111111111111111000 1 -- 补码 (移位后) // 空出的位置补的是 1 ,说明是算术右移(右边丢弃,左边补原来的符号位) // 实际值是用 原码 表示的 printf("%d\n", d); //算术右移 // 反码 +1 就是补码 ,补码 -1 就是反码: // 11111111111111111111111111110111 -- 反码 // 原码的符号位不变,反码按位取反得 原码 // 10000000000000000000000000001000 -- 原码 (-8) printf("%d\n", c); return 0; }
(有类似 除以2向下取整 的效果)
4.位操作符
(注:位操作符的操作数必须是整数)
1.整数在内存中存储的是补码
2.计算的时候也是使用补码计算的
& : 按(二进制)位与 -- 对应二进制位有0则为0,
两个同时为1,才为1
//位操作符 #include <stdio.h> int main() { int a = 3; // 00000000000000000000000000000011 -- 补码(正整数的原码、反码、补码相同) int b = -5; // 10000000000000000000000000000101 -- 原码 // 11111111111111111111111111111010 -- 反码 // 11111111111111111111111111111011 -- 补码 int c = a & b; // & -- 对应二进制位有0则为0,两个同时为1,才为1 // 00000000000000000000000000000011 -- a补码 // 11111111111111111111111111111011 -- b补码 // 得: // 00000000000000000000000000000011 -- 补码(两个补码计算出来的也是补码) // 这里计算出来的结果符号位是 0 ,说明是 正整数,三个码相同,所以这也是原码 printf("%d\n", c); // 所以结果是3 return 0; }
| : 按(二进制)位或 -- 对应二进制位有1则为1,两个同时为0,才为0
//位操作符 #include <stdio.h> int main() { int a = 3; // 00000000000000000000000000000011 -- 补码(正整数的原码、反码、补码相同) int b = -5; // 10000000000000000000000000000101 -- 原码 // 11111111111111111111111111111010 -- 反码 // 11111111111111111111111111111011 -- 补码 int c = a | b; // | -- 按(二进制)位或 -- 对应二进制位有1则为1,两个同时为0,才为0 // 00000000000000000000000000000011 -- a补码 // 11111111111111111111111111111011 -- b补码 // 得: // 11111111111111111111111111111011 -- 补码(两个补码计算出来的也是补码) // // 这里计算出来的结果符号位是 1 ,说明是 负整数,还要计算 反码 和 补码 // 11111111111111111111111111111010 -- 反码 // 10000000000000000000000000000101 -- 原码 printf("%d\n", c); // 所以结果是-5 return 0; }
^ : 按(二进制)位异或 -- 对应二进制位相同为0, 相异为1
//位操作符 #include <stdio.h> int main() { int a = 3; // 00000000000000000000000000000011 -- 补码(正整数的原码、反码、补码相同) int b = -5; // 10000000000000000000000000000101 -- 原码 // 11111111111111111111111111111010 -- 反码 // 11111111111111111111111111111011 -- 补码 int c = a ^ b; // ^ -- 按(二进制)位异或 -- 对应二进制位相同为0, 相异为1 // 00000000000000000000000000000011 -- a补码 // 11111111111111111111111111111011 -- b补码 // 得: // 11111111111111111111111111111000 -- 补码(两个补码计算出来的也是补码) // // 这里计算出来的结果符号位是 1 ,说明是 负整数,还要计算 反码 和 补码 // 10000000000000000000000000000111 -- 反码 // 10000000000000000000000000001000 -- 原码 printf("%d\n", c); // 所以结果是-8 return 0; }
^(按位异或) 的一些规律:
1. 变量 ^ 变量 = 0
2. 0 ^ 变量 = 变量
3. 满足交换律
实例:
//异或是支持交换律的 #include <stdio.h> int main() { int a = 3; int b = 5; printf("%d\n", a ^ a); // 0 (每一位都是相同的,32位全是0) printf("%d\n", a ^ 0); // a = 3 // 011 - 3 (a) // 000 - 0 // 011 -- 异或结果 :a = 3 printf("%d\n", a ^ b ^ a); // b = 5 // 011 - 3 (a) // 101 - 5 (b) // 110 -- 异或结果 // 011 - 3 (a) // 101 -- 异或结果 : b = 5 printf("%d\n", a ^ a ^ b); // b = 5 // a ^ a --> 0 // 0 ^ b --> b = 5 return 0; }
一道变态面试题:不使用临时变量,交换两个数的值
(^异或 不会造成进位,所以不会产生栈溢出,但可读性不高,不易理解)
//一道变态面试题:不使用临时变量,交换两个数的值 #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; }
练习:求一个整数存储在内存中的二进制中1的个数
#include <stdio.h> int main() { int input = 0; int count = 0; //统计 1 的个数 //输入 scanf("%d", &input); int i = 0; for (i = 0; i < 32; i++) { // 共32位,判断32次 int test = input & 1; //按位与1 取出32位最低位 //判断是不是1 if (test == 1) { count++; //是的话,统计加1 } //判断下一位: input >>= 1; //向右移一位,继续判断,使用循环 } //输出: printf("%d", count); 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; //选择赋值 //先把 y+1 赋给 x ,再把 x 赋给 a //更好的写法: x = y + 1; a = x; //这样写更加清晰而且方便一步步调试
复合赋值符
+= : 加等于
-= : 减等于
*= : 乘等于
/= : 除等于
%= : 模等于
>>= : 右移等于
<<= : 左移等于
&= : 按位与等于
|= : 按位或等于
^= : 按位异或等于
6.单目操作符
操作符只有一个操作数
! : 逻辑反操作
(if语句、while语句中、表达式中)
- : 负值
(改变数字正负)
+ : 正值
(正数默认自带+,常省略,不常用)
& : 取地址
(应用于指针)
sizeof : 操作数的类型长度(以字节为单位)
(不是函数,是操作符;计算的是类型创建变量的大小)
~ : 对一个数的二进制按位取反
(对补码32个bit位按位取反,1变成0,0变成1,取反后还是补码,要转换为原码)
-- : 前置、后置--
(常用)
++ : 前置、后置++
(常用)
* : 间接访问操作符(解引用操作符)
(应用于指针,告诉我们哪个变量是指针变量;
解引用:通过指针变量中存放的地址,找到指向的 空间/内容 )
(类型) : 强制类型转换
sizeof 和 数组
//sizeof 和 数组 #include <stdio.h> void test1(int arr[]) //传过来的首元素指针 ,一个 int*类型 大小是 4个字节 { printf("%d\n", sizeof(arr)); } void test2(char ch[]) //传过来的首元素指针 ,一个 char*类型 大小是 4个字节 { printf("%d\n", sizeof(ch)); } int main() { //整型数组: int arr[10] = { 0 }; printf("%d\n",sizeof(arr)); //使用 数组名 //40 - 计算整个数组的大小、单位字节 printf("%d\n", sizeof(int[10])); //使用数组的字符类型,包括[]和元素个数 test1(arr); //字符数组 char ch[10] = { 0 }; printf("%d\n", sizeof(ch)); //使用 数组名 //10 -- 字符数组中 sizeof(数组名) 计算 的是 字符个数 printf("%d\n", sizeof(int[10])); //使用数组的字符类型,包括[]和元素个数 //40 -- 一个 char类型元素 是 4个字节,10个就是40 test2(ch); return 0; }
~ 按位取反的运用,结合 & (按位与) 和 | (按位或)
(将 13 的 32bit位从右往左第5位,换为 1 ,再换为 0)
//~按位取反的运用,结合 & (按位与)和 | (按位或) //将 13 的 32bit位从右往左第5位,换为 1 ,再换为 0 #include <stdio.h> int main() { int a = 13; // 00000000000000000000000000001101 - 13 的 原、反、补码 // 按位或:有1就是1 // 00000000000000000000000000010000 - 1 << 4 // 00000000000000000000000000011101 -- 29 的 原、反、补码 a |= (1 << 4); // 这样就把第五位换成了 1 , 13 变成了 29 printf("%d\n", a); // 再把第五位换成 0 // 00000000000000000000000000011101 - 29 的 原、反、补码 // 00000000000000000000000000010000 - 1 << 4 // 11111111111111111111111111101111 - ~(1 << 4) 进行按位取反 // 按位或:两个都是 1 才是 1 // 00000000000000000000000000001101 -- 13 的 原、反、补码 a &= (~(1 << 4)); printf("%d", a); return 0; }
运用 ~按位取反 实现 多组输入
//运用 ~按位取反 实现 多组输入 #include <stdio.h> int main() { int a = 0; // scanf 读取失败返回的是EOF // 假设 scanf 读取失败,返回EOF ,即 -1 // -1 的补码: // 11111111111111111111111111111111 // ~ 按位取反后: // 00000000000000000000000000000000 // 得0,条件即为假 while (~scanf("%d", &a)) { printf("%d\n", a); } return 0; }
7. 关系操作符
(不能比较字符串、自定义类型)
> : 大于
>= : 大于等于
< : 小于
<= : 小于等于
!= : 不等于(用于测试“不相等”)
== : 相等(用于测试“相等”)
(=:是赋值 ; ==:是相等)
8. 逻辑操作符
&& : 逻辑与(并且:两个为真,才为真)
|| : 逻辑或(或者:多个中一个为真,就为真)
! : 逻辑非
演示:判断闰年
//判断闰年 #include <stdio.h> int main() { int y = 0; scanf("%d", &y); //1. 能被4整除,并且不能被100整除 //2. 能被400整除是闰年 if ( ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)) { printf("闰年\n"); } else { printf("不是闰年"); } return 0; }
=========================================================================
(下回揭晓:)
9. 条件操作符