一、最常用的算数运算符
运算符 | 含义 | 举例 | 结果 |
---|---|---|---|
+ | 正号运算符(单目运算符) | +a | a的值 |
- | 负号运算符(单目运算符) | -a | a的算数负值 |
* | 乘法运算符 | a * b | a和b的乘积 |
/ | 除法运算符 | a / b | a除以b的商 |
% | 取模(求余)运算符 | a % b | a除以b的余数 |
+ | 加法运算符 | a + b | a和b的和 |
- | 减法运算符 | a - b | a和b的差 |
使用时的注意事项:
1、除了 % 操作符之外,其他几个操作符可以作用于整数和浮点数。
2、对于 / 操作符,如果两个操作数都为整数,执行整数除法。而只要有浮点数,执行的就是浮点数除法。
int main()
{
int a = 3 / 5; //0.6
printf("%d\n", a); //a是整型,所以结果为0,不是小数。商0余3
return 0;
}
int main()
{
float a = 6 / 5;
printf("%f\n", a);
}
在这其中,6和5是整型,6/5是整型除法,其结果本身就是1。1赋值给a后就是1.000000。
问题一:如何得到一个商,并且它是小数?
int main()
{
float a = 6.0 / 5.0;//除号两边至少有一个数是浮点数,结果才能是小数。
printf("%f\n", a);//1.200000
}
在这里,编译器会默认6.0和5.0是double类型的,而a是float类型,由于6.0 / 5.0的结果仍是double类型,此时双精度的值赋值给单精度的a,容易丢失精度。 所以编译器会报一个警告出来。
答案:可以改成:
double a = 6.0 / 5.0;
printf("%lf\n", a);
//或者
float a = 6.0f / 5.0f;
printf("%f\n", a);
3、% 操作符的两个操作数必须为整数,返回的是整数之后的余数。
int main()
{
int a = 7 % 3; //求余数,商2余1
printf("%d\n", a);
return 0;
}
二、移位操作符
操作符 | 含义 |
---|---|
<< | 左移操作符 |
>> | 右移操作符 |
1.左移操作符 <<
示例1:
int main()
{
int a = 2;
int b = a << 1; //把a的二进制位向左移动一位,并把结果赋值给b
printf("b = %d\n", b);
return 0;
}
把2赋给a,而a是一个int类型的变量,占4个字节,即意味着32个bit位。
2的二进制序列为:(方框内共32位)
其中2的权重我们可以看作21。最右端的0的权重是20。把a的二进制位向左移一位的结果就是:
最左端的0位于方框外面,意味着被丢弃了,方框内共31位。所以要在最右端补上一个0。即:
此时,1位于右端第三位,权重为22=4,所以整个数字变成了4,即:b=4。
左移操作符的作用:
将一个数的二进制编码向左移动,高位溢出的舍弃,低位补零
即:左边丢弃,右边补0
左移2位——左边溢出2位,右边补上2个0。
2.右移操作符 >>
示例2:正数的二进制位右移
int main()
{
int a = 10;
int b = a >> 1; //把a的二进制位向右移动一位,并把结果赋值给b
printf("b = %d\n", b);
return 0;
}
10的二进制序列:00000000000000000000000000001010
其中:①是23=8,②是21=2。
右移一位后:
最右边溢出的0舍弃,①是22=4,②是20=1,③是需要补位的地方,也是二进制序列的开头。
在内存中存储一个整数的二进制序列的的时候,二进制位最左边的(最高位)是0,表示是一个正数,是1表示是一个负数。
右移操作符分为两种右移情况
(1)算数右移
右边丢弃,左边补原符号位。
(2)逻辑右移
右边丢弃,左边补0。
如果按算数右移,补原来的符号位,则补0;
如果按逻辑右移,则无论原本的数是正或负,都要在③所指的开头补0。
而因为10是正数,所以都必须补0,再补上0之后的二进制序列就是:
所以b的值为5。
示例3:负数的二进制位右移
int main()
{
int a = -1;
int b = a >> 1; //把a的二进制位向右移动一位,并把结果赋值给b
printf("b = %d\n", b);
return 0;
}
我们将负数:-1 存放在内存中(存放的是二进制的补码)
整数的二进制表示形式:其实有三种
原码:直接根据数值写出的二进制序列
反码:原码的符号位(左侧开头第一位)不变,其他位按原码取反
补码:反码+1
以 -1为例:
原码:10000000000000000000000000000001
反码:111111111111111111111111111111111110
补码:111111111111111111111111111111111111(最低位+1)
数据在内存中储存是以二进制的补码形式存储的。对于一个正整数来说,原码、反码和补码是相同的。
补码向右移动一位后,右侧的1溢出一位,如上图所示。那么问题一:③应该补1还是0?
若采用算数右移,由于-1是负数,所以③应该补1,结果没变,还是-1;
若采用逻辑右移,则③应该补0,成为一个正数。
答案:
当前的右移操作符采用的是:算数右移
问题二:当a=10的时候,把a的二进制位向右移动1为,得到b=5,那么a是否发生改变?
int main()
{
int a = 10;
int b = a >> 1;
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
答案:
a不发生改变,仍为10。b的值是表达式a >> 1的结果。
总结 :
左移操作符移位规则——左边抛弃,右边补0;
右移操作符移位规则:
(1)算数右移:右边丢弃,左边补原符号位。(最常采用);
(2)逻辑右移:右边丢弃,左边补0。
警告:对于移位运算符,不要移动负数位,这个标准是C语言未定义的。
例如:
int num = 10;
num >> -1; //错误