前言
一、算术操作符 + - * / %(运算符)
除法注意点:/ 除法 得到的是商
1.除法操作符的两个操作数都是整数的话,执行的是整数除法.用%d打印
2.除法操作符的两个操作数只要有一个浮点点数,执行的是小数除法.用%lf打印
% 取模(取余) 得到的是余数:
1.取模操作符的两个操作数必须是整数,返回的是整除之后的余数.
2.除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { //printf("%lf\n", 10 / 3); //不能这么写,因为两个操作数都是整数,应该用%d printf("%lf\n", 10 / 3.0); printf("%d\n", 10 / 3); }
二、移位操作符(操作数只能是整数)
移位操作符移动的是二进制的位
左移操作符: 左边丢弃,右边补0
数值的表示形式:
2进制,8进制,10进制,16进制
10进制的数据中,都是0-9的数字组成的
2进制的数据中,0-1的数字组成的
8进制的数据中,0-7的数字组成的
16进制的每一位是:
0~15
0 1 2 3 4 5 6 7 8 9 a b c d e f
比如:
表示数值10
1010----2进制
12-------8进制
10-------10进制
a---------16进制
数据的二进制表示:
整数的二进制表示形式有三种: 原码反,码,补码
原码:
把一个数按照正负直接翻译成二进制就是原码
最高位表示符号位,0表示正数,1表示负数
正整数的原码反码补码相同,
负整数的原码反码补码是要计算的
反码:
原码的符号位不变,其他位按位取反就是反码
补码:
反码+1
举例说明:
5,-5是整数,整数是存放在整型变量中的
一个整型变量是4个字节~32比特位
5:
原码 00000000000000000000000000000101
反码 00000000000000000000000000000101
补码 00000000000000000000000000000101
-5:
原码10000000000000000000000000000101
反码 11111111 11111111 11111111 11111010
补码 11111111 11111111 11111111 11111011
整数在内存中存储的是:二进制补码
4个比特位可以作为一个16进制数
例如:-5
补码 11111111 11111111 11111111 11111011
4个一组表示成16进制:
FF FF FF FB
正整数左移操作符举例说明:
3的二进制位:
00000000 00000000 00000000 00000011
向左移动一位,左边丢弃,右边补0
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 3; //3的二进制位:(正数的原码反码补码相同) //00000000 00000000 00000000 00000011 int b=a << 1; //向左移动一位,左边丢弃,右边补0 //b是正数,原码反码补码相同 //00000000 00000000 00000000 00000110 //b也就是6 printf("%d\n", b); printf("%d\n", a); return 0; }
负整数左移操作符举例说明:
原码------>补码:取反+1
补码------>原码:-1,取反 或者 取反+1
-3的二进制位左移一位:
由于整数在内存中存储的是补码,左移的时候实际上是将补码左移一位,右边补0.
首先求出-3的补码,左移补0之后得到的是结果的补码,还需要转换成原码,最后通过原码得出结果是多少
int main() { int a =-3; /*-3的二进制位: 10000000 00000000 00000000 00000011 -3的原码 11111111 11111111 11111111 11111100 -3的反码 11111111 11111111 11111111 11111101 -3的补码*/ // int b=a << 1; //-3的补码向左移动一位,左边丢弃,右边补0得到结果的补码 //11111111 11111111 11111111 11111010 b的补码 //11111111 11111111 11111111 11111001 b的反码 //10000000 00000000 00000000 00000110 b的原码 -6 printf("%d\n", b); printf("%d\n", a); return 0; }
右移操作符:
1.>> 算术右移:
右边丢弃,左边用原来的符号来填充
2.>> 逻辑右移:
右边丢弃,左边直接用0来填充
右移的时候,到底采用的是算术右移还是逻辑右移,是取决于编译器的!!
由下面的代码可以看出,vs2019编译器采用的是算术右移.
int main() { int a = -5; int b = a >> 1; //10000000 00000000 00000000 00000101 //11111111 11111111 11111111 11111010 //11111111 11111111 11111111 11111011 printf("b=%d\n", b); //算术右移 //11111111 11111111 11111111 11111101 //10000000 00000000 00000000 00000010 //10000000 00000000 00000000 00000011 -3 printf("a=%d\n", a); return 0; }
三、位操作符(操作数必须是整数)
位:指的是二进制位
int main() { int a = 3; int b = -5; int c = a & b; //00000000 00000000 00000000 00000011 3的补码 //10000000 00000000 00000000 00000101 -5的原码 //11111111 11111111 11111111 11111010 -5的反码 //11111111 11111111 11111111 11111011 -5的补码 //00000000 00000000 00000000 00000011 3的补码 //11111111 11111111 11111111 11111011 -5的补码 //00000000 00000000 00000000 00000011 a&b的补码,由于是正整数,原码也是这个 printf("%d\n", c); //3 int d = a | b; //按位或 //00000000 00000000 00000000 00000011 3的补码 //11111111 11111111 11111111 11111011 -5的补码 //11111111 11111111 11111111 11111011 a|b的补码,可以发现与-5的补码一样,所以他的原码数值等于-5 printf("%d\n", d); int e = a ^ b; //按位异或--对应的二进制位相同为0,相异为1 //00000000 00000000 00000000 00000011 3的补码 //11111111 11111111 11111111 11111011 -5的补码 //11111111 11111111 11111111 11111000 a^b的补码 //11111111 11111111 11111111 11110111 补码-1 //00000000 00000000 00000000 00001000 取反得到原码,结果为-8 printf("%d\n", e); }
交换a和b的值:
方法一:创建临时变量
int main() { int a = 3; int b = 5; printf("交换前:a=%d b=%d\n", a, b); int tmp = 0; tmp = 0; tmp = a; a = b; b = tmp; printf("交换后:a=%d b=%d\n", a, b); }
方法二:不创建临时变量
存在潜在风险,当数字过大时,加和之后可能超出整数的最大范围,有一定局限性
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; }
方法三:使用异或操作符,不创建临时变量
注意两个公式:
a^a=0
0^a=a
3^ 3^5=5
011 3
011 3
000 3^3
101 5
101 3^ 3^5结果5
353=5
异或支持交换律的
异或只能用于整数操作
异或只会产生0和1,不会进位出现2,所以永远不会溢出
int main() { int a = 3; int b = 5; printf("交换前:a=%d b=%d\n", a, b); a = a ^ b; b = a ^ b; //b=a^b^b=a^0=a a = a ^ b; //a=a^a^b=0^b=b printf("交换后:a=%d b=%d\n", a, b); }
编写代码实现:求一个整数存储在内存中的二进制中1的个数
思路点拨:
int main() { int a = 11; a & 1; //一个整数存储在内存中的二进制中1的个数 //只要每次右移,每次和1按位与得到最后一位看一下是不是1 a = a >> 1; //00000000 00000000 00000000 00001011 //00000000 00000000 00000000 00000001 //发现:最低位和1按位与,还是原来的数字 //如果想要得到这个数的最低位是几,只要和1按位与就可以了 //结果 //00000000 00000000 00000000 00000001 }
四、赋值操作符
=
复合操作符
int main() { int a = 3; a = a + 3; a += 3; a = a ^ 5; a ^= 5; a = a >> 3; a >>= 3; }
区分赋值和初始化:
int main() { int a = 3;//创建并初始化 a = 5;//赋值 return 0; }
六、单目操作符
!的应用场景:
int main() { int flag = 5; if (flag) { //flag为真做什么 } if (!flag) { //flag为假做什么 } return 0; }
&取地址操作符
int main() { int a = 10; &a;//&取地址操作符 int arr[10]; &arr;//取出数组的地址 }
解引用操作符(间接访问操作符)
*
int main() { int a = 10; int* p=&a; //取地址操作符 p里面存的是a的地址 *p = 20; //解引用操作符(间接访问操作符) int arr[10]; &arr; //取出数组的地址 return 0; }
sizeof
int main() { int a = 10; printf("%d\n", sizeof(a)); printf("%d\n", sizeof a); //变量求长度时的小括号可以不写,但是int必须写 printf("%d\n", sizeof(int)); int arr[10] = { 0 }; printf("%d\n", sizeof(arr)); //数组的长度 printf("%d\n", sizeof(arr[0])); //数组元素的长度 int sz = sizeof(arr) / sizeof(arr[0]); //数组元素的个数 printf("%d\n", sz); return 0; }
~的用法
对一个数的二进制位按位取反
int main() { int a = 0; //00000000 00000000 00000000 00000000 //~a:按位取反 //11111111 11111111 11111111 11111111 补码(内存里面的字都是补码) //11111111 11111111 11111111 11111110 反码 //00000000 00000000 00000000 00000001 原码 -1 printf("%d\n", ~a); //-1 }
下面举例灵活应用单目操作符:
int main() { int b = 3; //00000000 00000000 00000000 00000011 b //如何把上面的数字倒数第四位变成1? //00000000 00000000 00000000 00000011 b //用b| //00000000 00000000 00000000 00001000 (1 << 3) b |= (1 << 3); //00000000 00000000 00000000 00001011 b=11 printf("%d\n", b); //11 //如何将下面的数字 //00000000 00000000 00000000 00001011 //倒数第四位还原成0?? // // 0&1=0 1&1=1 // 所以将原数字与下面的数字按位与,就可以将倒数第四位还原成0 //11111111 11111111 11111111 11110111 这个数字怎么表示呢? //我们发现它刚好是00000000 00000000 00000000 00001000 (1 << 3)取反可以得到 // b &= (~(1 << 3)); printf("%d\n", b); //3 return 0; }
小知识点:
scanf读取失败的时候返回-1
原码
10000000 00000000 00000000 00000001
反码
11111111 11111111 11111111 11111110
补码
11111111 11111111 11111111 11111111
~按位取反之后:全0
00000000 00000000 00000000 00000000
此时就变成:
while(0)不进入循环,scanf读取失败
前置++ --:在使用之前++ - -
后置++ --:在使用之后++ - -
int main() { int a = 10; int b = a++; printf("%d\n", a); printf("%d\n", b); //后置++,先使用,再++ //相当于int b=a;a=a+1; }
后置++
int main() { int a = 10; printf("%d\n", a++);//先使用,后++ printf("%d\n", a); }
前置++
int main() { int a = 10; printf("%d\n", ++a); //先++,再使用 printf("%d\n", a); }
前置- -和后置- -同上面一个道理
(类型) 强制类型转换
仅仅是临时改变数据类型的一个状态
int main() { float a = 3.14f; int b = (int)a; //强制性类型转换 return 0; }
sizeof()和数组
1.sizeof()内部单独放一个数组名,表示整个数组,所以(1)(3)处的值为40,10
2.注意:数组在传参的时候,实际上是传过去首元素的地址
void test1(int arr[]) //整形的地址放到整形的指针里面,int本质是int*,存放地址,指针变量的大小都是四个字节或者八个字节 { printf("%d\n", sizeof(arr));//(2)X86环境下指针的大小是4,X64环境下指针的大小是8 } void test2(char ch[]) //char本质是char*,存放地址 { printf("%d\n", sizeof(ch));//(4)X86环境下指针的大小是4,X64环境下指针的大小是8 } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//(1) 计算的是整个数组的大小:40 printf("%d\n", sizeof(ch));//(3) 计算char类型的数组大小:10 test1(arr); test2(ch); return 0; }
七、关系操作符
在编程中注意==和=不要使用错误
八、逻辑操作符
&& 逻辑与 并且的意思 只关注真假
| | 逻辑或 或者的意思 只关注真假
& 按位与 二进制位进行计算
| 按位或 二进制位进行计算
int main() { int a = 3 && 5; int b = 3 && 0; printf("%d\n", a); printf("%d\n", b); int c = 1 || 0; int d = 0 || 0; printf("%d\n", c); printf("%d\n", d); return 0; }
&& 操作符左边为假,右边不再计算
| | 操作符左边为真,右边不再计算
举例说明:
int main() { int i = 0, a = 0, b = 2, c = 3, d = 4; i = a++ && ++b && d++; //0&&...a=0,后面不用计算 printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d); return 0; }
int main() { int i = 0, a = 1, b = 2, c = 3, d = 4; i = a++ && ++b && d++; //1&&3&&4 printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//2 3 3 5 return 0; }
int main() { int i = 0, a = 1, b = 2, c = 3, d = 4; i = a++ || ++b || d++; //1||...或运算,左边有一个1,右边不用计算 printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//2 2 3 4 return 0; }
int main() { int i = 0, a = 0, b = 2, c = 3, d = 4; i = a++ || ++b || d++; //0||2...右边不用计算 printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);//1 3 3 4 return 0; }
这篇文章的内容你是否有所收获呢?小伙伴们,记得点赞收藏博客,关注后续的C语言操作符(下)内容哦~😉😉💕