目录
[编辑 2.1.1--10进制转2进制数字](#编辑 2.1.1--10进制转2进制数字)
5.2.1--练习1:不能创建临时变量(第三个变量),实现两个整数的交换。
5.2.2--练习2:编写代码实现:求一个整数存储在内存中的二进制中1的个数
[六.逗号表达式,下标访问 [],函数调用 ()](#六.逗号表达式,下标访问 [],函数调用 ())
[6.2--[] 下标引用操作符](#6.2--[] 下标引用操作符)
[6.3--() 函数调用操作符](#6.3--() 函数调用操作符)
前言:上篇文章我们学习了函数递归的相关知识,今天我们将继续操作符的详解学习的第一部分,让大家对于c语言的操作符产生一些新的认识和想法。

一.操作符的分类
1.算术操作符:+ , - , * , / , %
2.移位操作符:<< , >>
3.位操作符:&, | ,^
4.赋值运算符:= , += ,- = ,*= ,/= ,%= ,<<= ,>>= , &= , |= ,^=
5.单目操作符:!,++ ,- - ,& ,* ,+(正),-(负) ,~ ,sizeof ,(类型)
6.关系操作符:> ,>= ,< ,<= ,== ,!=
7.逻辑操作符:&& ,|| ,!
8.条件操作符:exp1?exp2:exp3
9.逗号表达式:,
10.下标引用:【】
11.函数调用:()
12.结构成员访问:. , ->
上述的操作符中,有部分在之前的学习中有提到过,今天继续介绍一部分,其中有部分操作符和二进制有关系,我们先来学习一下二进制和进制转换的相关知识;
二.二进制和进制转换
2.1--2进制转10进制
--其实10进制的123表示的值是一百二十三,为什么是个值呢?其实10进制的每一位都是有权重的,进制的数字从右向左是个位,十位,百位......,分别每一位的权重是10^0,10^1,10^3;
如下图:

2进制和10进制是类似的,只不过2进制的每一位的权重,从右向左是:2^0,2^1,2^2......
如果是2机制的1101,该怎么理解呢?
2.1.1--10进制转2进制数字

简单的10进制数字可以直接通过凑的方法来转2进制;
2.2--2进制转8进制和16进制
2.2.1--2进制转8进制
--8进制的每一位是0~7的,0~7的数字,各自写成2进制,最多有3个2进制位就足够了,比如7的二进制是111,所以2进制转8进制的时候,从2进制序中右边低位开始向左每3个二进制位会换算一个8进制位,剩余不够3个2进制位的直接换算。
如:2进制的01101011,换成8进制:0153,8进制表示的时候前面加0;0开头的数字,会被当作8进制。

2.2.2--2进制转16进制
--16进制的数字每一位是0~9,a~f的,0~9,a~f的数字,各自写成2进制,最多有4个二进制位就足够了。比如f的二进制是1111,所以在2进制转16进制的时候,从2进制序列中右边低位开始向左每4个2进制位会换算一个16进制位,剩余不够4个2进制位的直接换算。
如:2进制的01101011,换成16进制:0x6b, 16进制表示的时候前面加0x。

三.原码,反码,补码
--整数2进制的表示方法有三种,即原码,反码和补码(32位)
有符号整数 的三种表示方法均有符号位 和数值位 两部分,2进制序列中,最高位的1位是被当做符号位的,剩余的都是数值位,符号位都是用0表示正,用1表示负。
正整数的原,反,补码都相同。
负整数的三种表示方法各不相同,变换方式如下。
原码: 直接将数值按正负数的形式翻译成二进制得到的就是原码。
反码: 原码的符号位不变,其他位次按位取反就可以得到反码。
补码: 反码+1就得到补码。
++补码反过来得到原码也是可以使用:取反,+1的操作++ ++。++
对于整型来说:数据存放内存中其实存放的是补码。
这是为什么呢?
--在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码和原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
四.移位操作符
<< 左移操作符
>> 右移操作符
注意:移位操作符的操作数只能是整数
4.1--左移操作符
--移位规则:左边抛弃,右边补0;
int num=10; 左移一位演示
4.2--右移操作符
1.逻辑右移:左边用0填充,右边丢弃
2.算术右移:左边用原该值的符号位填充,右边丢弃
--是逻辑右移还是算术右移取决于编译器,VS2022中采用的是算术右移。
int num=-1;逻辑右移一位演示
int num=-1;算术右移一位演示

注意:对于移位运算符,不要移动负数位,这个是标准没定义的。
五.位操作符:&,|,^,~
5.1--位操作符的理解与使用
位操作符有:
cpp
& //按位与
| //按位或
^ //按位异或
~ //按位取反
注意:他们的操作数必须是整数。
我们可以通过一串代码直观了解一下这几个位操作符,注意看注释,来理解是如何使用的。
cpp
#include <stdio.h>
int main()
{
int num1 = -3;
int num2 = 5;
// &--按位与--对应的二进制位进行与运算,只要有0则为0,两个同时为1,才为1
printf("%d\n", num1 & num2);
// |--按位或--对应的二进制位进行或运算,只要有1则为1,两个同时为0,才为0
printf("%d\n", num1 | num2);
// ^--按位异或--对应的二进制位相同为0,相异为1
printf("%d\n", num1 ^ num2);
// ~--按位取反--二进制位0变为1,1变为0
printf("%d\n", ~0);
return 0;
}
5.2--相关练习
5.2.1--练习1:不能创建临时变量(第三个变量),实现两个整数的交换。
cpp
int main()
{
int a = 0;
int b = 0;
printf("请输入两个整数:");
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
a = a ^ b;// a'=a^b;
b = b ^ a;// b=b^a'=b^a^b=a;
a = a ^ b;// a=a'^b=a^b^a=b;
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
在这里补充两个重要结论:
cpp
a ^ a = 0;
a ^ 0 = a;
//且异或支持交换律;
//如:3 ^ 6 ^ 6 = 3
// 6 ^ 3 ^ 6 = 3
5.2.2--练习2:编写代码实现:求一个整数存储在内存中的二进制中1的个数
cpp
int count_bits1_of_n(int n)
{
//int count = 0;
//for (int i = 0;i < 32;i++)
//{
// if(((n >> i) & 1)==1)
// count++;
//}
//return count;
//优化方案
int count = 0;
while (n)
{
n = n & (n - 1);
//用一次这个公式就相当于将一个1变为0,即去掉一个1,当n为0时,1全部去掉,且刚好循环结束
count++;
}
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = count_bits1_of_n(n);
printf("%d", ret);
return 0;
}
注意:n=n&(n-1)这个公式的用法在这里很重要。
5.2.3--练习3:二进制位置0或者置1
编写代码将13二进制序列的第五位修改为1,然后再改回0;
cpp
int main()
{
int a = 13;
a = a | (1 << 4);
printf("a=%d\n", a);
a &= ~(1 << 4);
printf("a=%d\n", a);
return 0;
}
六.逗号表达式,下标访问 [],函数调用 ()
6.1--逗号表达式
cpp
exp1, exp2, exp3, ...expN
逗号表达式,就是用逗号隔开的多个表达式。从左向右依次执行 。整个表达式的结果是最后一个表达式的结果。
cpp
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?--c是13;
//代码2
if(a = b + 1,c = a / 2,d > 0)
//代码3
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
//...
a = get_val();
count_val(a);
}
如果使⽤逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
6.2--[] 下标引用操作符
--操作数:一个数值名 + 一个索引值(下标)
cpp
int arr[10]={0};//创建数组
arr[9] = 10;//运⽤下标引⽤操作符。
[ ]的两个操作数是arr和9。
6.3--() 函数调用操作符
cpp
int Add(int x,int y)
{
return x + y;
}
int main()
{
printf("hehe");//()是函数调用操作符
int r = Add(3,5);//()是函数调用操作符
// ()也是有操作数:函数名和参数都是()的操作数
//()最少有一个操作数,就是函数名本身
return 0;
}
结语:操作符详解的第一部分就到此结束了,在下一篇文章中我将继续为大家分享操作符详解的一些其他知识点,感谢大家的支持!