操作符(1)
Suzhou 40 0
算术操作符
除法运算符
#include <stdio.h>

int main()
{
    int a = 5 / 2;
    printf("a = %d\n", a);
    return 0;
}
a = 2

上述代码除号两端的数字都是整数,整数除法不会得到小数。如果除号两边的任意一个数字是小数形式,计算出的结果也是浮点数,类型改成double可以计算出小数(默认打印小数点后6位)。

#include <stdio.h>

int main()
{
    double a = 5 / 2.0;
    printf("a = %lf\n", a);
    return 0;
}
a = 2.500000

取模运算符
#include <stdio.h>

int main()
{
    int a = 5 % 2;
    printf("a = %d\n", a);
    return 0;
}
a = 1

取模运算中左操作数和右操作数都要是整数。 ::: tip 除了%操作符之外,其他的几个操作符可以作用于整数和浮点数。 对于/操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。 %操作符的两个操作数必须为整数。返回的是整除之后的余数。 :::

移位操作符
#include <stdio.h>
int main()
{
    int a = 16;
    int b = a >> 1;  // >> -- 右移操作符,移动的是二进制位
    printf("b = %d", b);
    return 0;
}
b = 8

上述代码中,16的二进制数是10000,a是整型变量,有32个比特位,16在变量a中的排列如下:00000000000000000000000000010000。 a的二进制序列向右移动一位,最右边的数字被丢弃。通过下面的代码确定了编译器采用了算术右移,左边补0,此时16在变量b中的排列如下:00000000000000000000000000001000。即二进制数中的数字1右移一位,十进制数变为8。

::: danger 移位运算符不要移动负数位和浮点数位,如a >> -1、a >> 1.5。 这个标准未定义,编译器执行结果会出错。 :::


右移操作符

::: tip 右移操作符的两种方法: 1.算术右移:右边数字丢弃,左边补原符号位(正数补0,负数补1) 2.逻辑右移:右边数字丢弃,左边补0 ::: 判断编译器是算术右移还是逻辑右移:

#include <stdio.h>
int main()
{
    int a = -1;
    int b = a >> 1;  // >> -- 右移操作符,移动的是二进制位
    printf("b = %d", b);
    return 0;
}
b = -1

如上代码,当a为-1,如果编译器是逻辑右移,前面补0,a右移后变为正数b。如果编译器是算术右移,左边补原符号位1,a右移后的b还是负数。


左移操作符

左移操作符:左边丢弃,右边补0。

#include <stdio.h>
int main()
{
    int a = 5;
    int b = a << 1;  // << -- 左移操作符,移动的是二进制位
    printf("b = %d", b);
    return 0;
}
b = 10

如上代码,当a为5,二进制表示为:00000000000000000000000000000101(2的2次方+2的0次方),左移1位后为10,二进制表示为:00000000000000000000000000001010。(2的3次方+2的1次方)

补充

::: warning 整数的二进制表示有3种:源码、反码、补码。 存储到内存中的是补码。移位时也是移动补码。首先要写出a的补码。 正整数的原码反码补码是相同的。 负整数的原反补(以-1为例): 源码:10000000000000000000000000000001。负数最高位符号位为1,最后一位表示实际数字1。 反码:11111111111111111111111111111110。反码是源码的基础上,符号位不变,其余位置按位取反。 补码:11111111111111111111111111111111。反码+1。 :::


位操作符
&按位与

&是按二进制位与,需要先写出整数的二进制形式(补码)。 对应二进制位只要有一个是0就为0,两个都为1才为1。

#include <stdio.h>
int main()
{
    int a = 3;      //000000000000000000000011
    int b = 5;      //000000000000000000000101
    int c = a & b;  //000000000000000000000001
    printf("c = %d", c);
    return 0;
}
c = 1
|按位或

|是按二进制位或,需要先写出整数的二进制形式(补码)。 对应二进制位只要有一个是1就为1,两个都为0才为0。

#include <stdio.h>
int main()
{
    int a = 3;      //000000000000000000000011
    int b = 5;      //000000000000000000000101
    int c = a | b;  //000000000000000000000111
    printf("c = %d", c);
    return 0;
}
c = 7
^按位异或

^是按二进制位异或,需要先写出整数的二进制形式(补码)。 对应二进制相同为0,相异为1。(同假异真) 交换两个变量的值,不使用新变量:

#include <stdio.h>
int main()
{
    int a = 5;
    int b = 3;
    printf("a = %d,b = %d\n", a, b);
    a = a + b;  //a = 8
    b = a - b;  //b = 5
    a = a - b;  //a = 3
    printf("a = %d,b = %d", a, b);
    return 0;
}
a = 5,b = 3
a = 3,b = 5

::: warning 上述代码可能存在的问题是,a和b都是整型值,占4个字节(32比特),如果a和b的值非常大,各自没有超出整型值所能容纳的最大值,但是相加后超出了,就会发生溢出,丢失部分内容,后面相减时会出错。 :::

#include <stdio.h>
int main()
{
    int a = 5;  //011
    int b = 3;  //101
    printf("a = %d,b = %d\n", a, b);
    a = a ^ b;  //a = 110
    b = a ^ b;  //b = 011
    a = a ^ b;  //a = 101
    printf("a = %d,b = %d", a, b);
    return 0;
a = 5,b = 3
a = 3,b = 5

上述代码可以描述为:a和b异或产生密码a,密码a和b异或得到原来的a,放在b中,密码a和b(原来a)异或得到原来的b,放在a中,完成a和b的交换。 ::: warning 异或的方式交换两个值时,代码执行效率不高,可读性差。 在实际操作中,两个变量值的交换,一般采用引入临时变量的方法。 :::

练习

求一个整数存储在内存中的二进制数中1的个数 ::: tip 代码思路: 数字的二进制形式和1做按位与,如果二进制数最后一位为1,按位与后的结果就是1。 然后使用右移操作符每次将二进制数的最后一位丢掉,在和1做按位与,直到32位比特的每一个数字都和1做了按位与。 统计32次按位与后结果为1的次数,就是这个整数存储在内存中的二进制数中1的个数。 :::

#include <stdio.h>
int main()
{
    int num = 0;
    scanf("%d", &num);
    int i = 0;
    int count = 0;
    for (i = 0; i < 32; i++)
    {
        if ((num >> i & 1) == 1)
        {
            count++;
        }
    }
    printf("1的个数为%d", count);
    return 0;
}
3
1的个数为2

#####赋值操作符 赋值操作时不建议使用如下的连续赋值:

#include <stdio.h>
int main()
{
    int a = 10;
    int b = 5;
    int c = 8;
    a = b = c + 2;
    printf("%d", a);
}

拆分后更容易阅读。


复合赋值符包括如下:

+=  -=  *=  /=  %=  >>=  <<=  &=  |=  ^=

评论区

索引目录