文章目录
操作符的分类:
算术操作符:+ 、- 、* 、/ 、%
移位操作符: << >>
位操作符: & | ^ ~
赋值操作符: = 、+= 、-= 、 = 、/= 、%= 、<<= 、>>= 、&=、|=、^=
单⽬操作符:!、++、–、&、、+、、~ 、sizeof(类型)
关系操作符: > 、>= 、< 、<= 、== 、 !=
逻辑操作符:&& 、||
条件操作符:? :
逗号表达式:,
下标引⽤:[]
函数调用:()
一.进制转换
我们常说的有二进制,八进制,十进制,十六进制,它们其实都是数值的不同表示形式而已,本篇文章着重介绍二进制数
15的二进制:1111
15的八进制:17
15的十进制:15
15的十六进制:F
首先介绍十进制数:
- 每一位都是0~9
- 满10进1
因此二进制数:
- 每一位都是0~1
- 满2进1
八进制数:
- 每一位都是0~7
- 满8进1
十六进制数:
- 每一位都是0~F(0 ~ 9 ,10用A表示,11用B表示,直到15用F表示)
- 满16进1
问:十进制数123为什么等于123呢?
其实十进制数每一位都是有权重的,十进制数从右向左依次是个位,十位,百位…依分别对应的权重是100,101,102…
1.二进制与十进制的互换
1.1十进制数转换为二进制数
第一种方法就是除2取余法(最后得到的余数从下往上取)
如:10的二进制为1010
第二种方法
把数拆分成几个2的次方数的和,如下图:权重23就在第四个位置放1(从右往左数),权重21就在第二个位置放1,其余放0
2.2二进制数转换成十进制数
2.二进制与八进制和十六进制的转换
2.1二进制转换成十六进制
2.2十六进制转换成二进制
2.2二进制转换成八进制
二.原码,反码,补码
整数的二进制表示方法有3种:原码,反码,补码
有符号整数的表示方法由符号位和数值位构成,二进制中最高位是符号位,其余都是数值位
若是正数,符号位用0表示;若是负数,符号位用1表示
正数的原码,反码,补码都相同
负数的反码由原码按位取反(若是0,换成1;若是1,换成0)得到
补码由反码加1得到
补码也可以取反,加1得到原码
byte是字节,1字节等于8比特位,也就是8个二进制位
1byte = 8bit
1kb = 1024byte
1mb = 1024kb
…
这里就不介绍正数的原,反,补了
三.移位操作符
计算机内存中存放的是补码,也是用补码用来计算的
移位操作符的操作数必须是整数
1.左移操作符
左边丢弃,右边补0
这里得到了num左移1位后的补码,然后在把它转换成原码(这里我就不写原码了)就得到了结果r = 20
2.右移操作符
右移操作符有2种移位规则:
1.逻辑右移:左边补0,右边丢弃
2.算术右移:左边用原值的符号位填充,右边丢弃
这里讲一下算术右移(我用的是vs2022,执行的是算术右移)
四.位操作符
位操作符的操作数必须是整数
位操作符有:
- &:按位与 全1则1
- |:按位或 有1则1
- ^:按位异或 相同为0,相异位1
- ~:按位取反
1.按位与&
计算 3 & -5
两个都是1的时候则是1
2.按位或|
计算3 | -5
两个中有1 则是1
3.按位异或^
两个相同则为0,不同则为1
4.按位取反~
按位取反就是0与1互换
五.位操作符的实践
题目一:不能创建临时变量(第三个变量),实现两个整数的交换
法一:
然而这种方法是有风险的,如果a和b的值很大,a+b就可能会溢出
法二:
a ^ a = 0;
a ^ 0 = a;
3 ^ 5 ^ 3 = 3 ^ 3 ^ 5 = 5 //异或支持交换律
因此代码如下:
题目二:找出单身狗问题,有一组数,只有一个数字出现1次,其它数字都是出现2次,请找出出现1次的数字
我们也可以用^来实现,假如这组数字为:1 2 3 4 5 1 2 3 4
把所有的数字都异或就能得到5了
即 1^2^3^4^5^1^2^3^4
题目三:求⼀个整数存储在内存中的⼆进制中1的个数。(即补码中1的个数)
int main()
{
int num = 0;
scanf("%d", &num);
int i = 0;
int count = 0; //计数
for (i = 0; i < 32; i++) //有32位,所以要执行32次
{
if (((num >> i) & 1) == 1)
{
count++;
}
}
printf("%d\n", count);
return 0;
}
我们采用 num&1 来实现,若num的最低位是1,count++
如果num的最低位是1,num&1的结果就是1;如果num的最低位是0,num&1的结果就是0,因此结果是1的话我们的count就加1,然后右移1位,把第二位移到最低位,再次&1,判断结果是否等于1,然后右移2位,把第三位移到最低位…
上述代码虽能实现,但每次都要执行32次,效率比较低
优化后的代码:(有几个1就执行几次)
int main()
{
int num = 0;
scanf("%d", &num);
int count = 0; //计数
while (num)
{
num = num & (num - 1);
count++;
}
printf("%d\n", count);
return 0;
}
根据图中可以看到,每执行一次n = n & (n-1) 就去掉了低位的1,去掉后我们就count++
六.逗号表达式
exp1,exp2,exp3,.....expN
逗号表达式就是用逗号隔开的多个表达式
计算规则是:从左往右依次执行,最后一个表达式的结果就是整个表达式的结果
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1); //c=13
c的值就是b=a+1的结果,从左往右依次计算,其中a=b+10使a的值为12了,所以最后的表达式的结果就为13
七.下标访问[]与函数调用()
1.下标引用操作符[]
int arr[10];//创建数组
arr[9] = 10;//实⽤下标引⽤操作符。
[ ]的两个操作数是arr和9
2.函数调用操作符()
接受⼀个或者多个操作数:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数。
八.操作符的属性:优先级和结合性
1.优先级
a + b * c;
上述示例中,有加法和乘法两个操作符,根据优先级乘法高于加法,因此先计算b * c
2.结合性
当优先级相同时就需要考虑结合性来决定计算的先后顺序了,操作符的结合性又分为左结合(从左往右执行)和右结合(从右往左执行),大部分都是左结合。
a * b / c;
上述示例中,乘法和除法优先级相同,两者都是左结合操作符,因此先计算a * b
我们可以把想要先计算的放在()里面,就像数学那样有括号的先计算括号里面的。
下面附一张图:
九.表达式求值
1.整型提升
计算机中执行运算的操作数至少是int,凡是小于int类型的都要转换,这就是整型提升。如:char,short类型
char a = -10;
char b = 120;
char c = a + b;
printf("%d",c);
上述示例中,你想的是打印出a+b的值,结果是110,对吧?
因为a和b都是char类型,所以需要整型提升 (1个字节提升为4个字节)
那又如何实现呢?
1.有符号整数的提升是按照其变量的数据类型的符号位来提升的,符号位是0,高位就补0;符号位是1,高位就补1
2.无符号整数的提升,高位补0
下面用图来说明上述打印的结果:
整型提升后的a+b:
最后打印:
最后打印的时候是以%d的形式打印的,而c是char类型,因此还要发生一次整型提升,最终打印出110。不要认为是直接相加的值,看下面再举个例子
注:下图把a的值改为10,结果就不是130了
2.算术转换
某个操作符的操作数属于不同类型,那么除非其中一个操作数的类型转换成另一个操作数的类型,否则无法进行计算。
举个简单的例子:
int a = 1;
float b = 2.1;
a+b;
//a会转换成float类型的在进行计算
以下是转换的优先级 从高到低 低类型转换成高类型
long double
double
float
unsigned long int
long int
unsigned int
int
十.问题表达式
a*b + c*d + e*f
我们想到的肯定都是先计算乘法,然后从左往右计算加法。但计算机的执行顺序就不唯一了
第一种顺序:
第二种顺序:
c + --c;
上述示例中,第一个c是有歧义的。- -的优先级高于+,但是我们不知道+的左操作数是–c之后的c,还是没有–后的c。
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
#include <stdio.h>
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);//输出多少?
return 0;
}
上述示例中,我们只能得知操作符*和-的运算顺序,但是3个fun()的执行顺序是不清楚的。
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
上述示例和前面第一个列举的类似,第一个+与第三个++的执行顺序不清楚。
执行第一步,++i后i等于2,执行第二步,++i后i等于3,执行第三步,即3+3,执行第四步,++i后i等于4,执行第五步,3+3+4,结果为10
执行第一步,++i后i等于2,执行第二步,++i后i等于3,执行第三步,++i后i等于4,执行第四步,4+4,执行第五步,4+4+4,结果为12
建议:不要写太冗长的表达式