算术操作符

+ - * / %

  这几个操作符都比较简单,需要注意的是,+-*/这几个操作符可以用于整数和浮点数。%这个操作符的两个操作数只能为整型。

  在程序中,若是两个操作数都是整型,那么得到的结果也只能是整型,编译器会自动截断后面的小数部分。若是某个操作数为浮点型,那么结果只能为浮点型。

移位操作数

  左移操作数:<<

  左移规则:将数字在内存中的二进制表示左移一位,右边补0, 等同于将变量中的数本身乘以2并且将值重新赋给变量。

#define _CRT_SECURE_NO_WARNINGS
//添加头文件
#include <stdio.h>
#include <stdlib.h>
//主函数,函数入口
int main()
{
    int num = 10;
    //未移位前:00000000000000000000000000001010
    printf("%d\n", num << 1);//打印20
    //左移一位后:00000000000000000000000000010100
    return 0;
}

  右移操作数:>>

  右移规则:

    ①逻辑右移:移位操作后,左边用零填充,右边舍弃。

    ②算术右移:移位操作后,左边根据操作数的正负来确定用0/1来填充,右边舍弃。

#define _CRT_SECURE_NO_WARNINGS
//添加头文件
#include <stdio.h>
#include <stdlib.h>
//主函数,函数入口
int main()
{
    int num = -1;
    //右移位前:11111111111111111111111111111111
       //右移移位后:11111111111111111111111111111111
       //打印-1    
       printf("%d\n", num >> 1);
    return 0;
}

  移位操作相当于是对数据乘以二的n次方或者除以二的n次方,例如,左移4位,相当于是对数据乘以2的四次方;

  但是由于移位操作是直接作用于二进制代码的,是直接作用在机器指令上的,所以它的运算速度比普通乘除要快许多;

  不过,虽然运算速度快,但是在写代码的时候尽量避免使用移位操作符,因为①:会影响可读性,相对于一般的项目,完成项目的效率可要比代码最终的执行效率重要太多了;

          ②:现在一些高级的编译器,会将你代码中的乘除操作自动转换为移位操作,还是比较人性化的。

位操作符

  位操作符是作用在二进制代码的每一位上

  按位与:&;1&1为1,其余均为0;

  例:(1)清零。如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

    (2)取一个数中指定位,方法:找一个数,对应X要取的位,该数的对应位为1,其余位为零,此数与X进行“与运算”可以得到X中的指定位。

#define _CRT_SECURE_NO_WARNINGS
//添加头文件
#include <stdio.h>
#include <stdlib.h>
int BitOneCount(int num)
{
    int count = 0;
        //一个整形最多有32位,因此我们需要从第一位开始进行32个循环逐步计数
    for (int i = 0; i < 32; i++)
    {
                //右移验证最低位是否是1,是1则计数器+1
        if (num >> i & 1 != 0)
        {
            count++;
        }
    }
    return count;
}
//主函数,函数入口
int main()
{
    printf("%d\n", BitOneCount(-1));//打印32
    return 0;
}

 

  按位或:|;0|0为0,其余均为1;

  例:(1)常用来对一个数据的某些位置1,方法:找到一个数,对应X要置1的位,该数的对应位为1,其余位为零。此数与X相或可使X中的某些位置1。

  按位异或:^;两操作数相同为0,不同为1;

  例:(1)使特定位翻转,要使哪几位翻转就将与其^运算的该几位置为1即可。

    (2)使特定位保留,因为原数中的1与0进行^运算得1,0^0得0,故保留原数。

    (3)交换两个值,不用临时变量,

     假如a=3,b=4,想将a和b的值互换,可以用以下赋值语句实现:a=a^b;b=b^a;a=a^b;

      原理:①执行前两个赋值语句:“a=a^b;”和“b=b^a;”相当于b=b^(a^b)。而b^a^b等于a^b^b。b^b的结果为0,因为同一个数与本身相^,结果必为0。因此b的值等于a^0,即a,其值为3。

        ②再执行第三个赋值语句:a=a^b。由于a的值等于(a^b),b的值等于(b^a^b),因此,相当于a=a^b^b^a^b,即a的值等于a^a^b^b^b,等于b。

    (4)快速判断两个值是否相等

     举例1: 判断两个整数a,b是否相等,则可通过下列语句实现: return ((a ^ b) == 0);

  按位取反:~;1变0,0变1;

赋值操作符

  赋值运算符:=;这是最常见的,这代表了赋值的意思,并不是判断相等的,一定要注意这一点,一不小心就搞错了。

  赋值操作符还有一些变形操作:

a+=1相当于a=a+1
a*=2相当于a=a*2

 

单目操作符

  逻辑反:!;此操作符可以将逻辑语句进行取反操作。

  负值/正值:-/+;此操作符与加减不同,可以将数值取到对应的相反数;

  取地址:&;可以将变量在内存中的地址取到,scanf()操作的时候就是在取地址;在指针操作中也会经常会见到;

  sizeof:sizeof();可以求出变量在内存中所占字节的大小;

  自增/自减:++/--;一个变量的自增自减相当于是,将这个数减一或加一再赋给这个这个变量;需要注意的是前置与后置的区别:前置:先加加或减减之后,再使用;

  后置:先使用,然后加加或减减;

  这里以++的前置和后置为例:

#include<stdio.h>
#include<stdlib.h>

int main() {
    int a = 1;
    int b = 2;
    b = a++;
    printf("%d %d\n", a, b);
        //输出结果:a=2;b=1;
    int c = 2;
    int d = 4;
    d = ++c;
    printf("%d %d\n", c, d);
        //输出结果:c=3;d=3;
    return 0;
}

 

  解引用:*;这个操作符常与指针连用,可以通过解引用指针找到指针所指变量中的数据;

  强制类型转换:(类型);若一个变量定义的类型无法满足某个表达式时,就可使用此操作符来实现目的。

关系操作符

  关系操作符是用来判断表达式的逻辑关系的,若满足表达式,则为真;若表达式不满足,则为假。

//大于
> 
//大于等于
>= 
//小于
< 
//小于等于
<= 
//等于
== 
//不等于
!=

 

逻辑操作符

  此操作符用来实现一个逻辑表达式的连接

  逻辑与:&&;前后表达式都为真才为真,否则为假

  逻辑或:||;前后表达式满足一个即为真,若都为假,才为假

条件操作符

  也叫作三目运算符,具体形式如下:

表达式 1 ? 表达式 2 : 表达式 3;

 

  含义:若表达式1为真,则实行表达式2;若为假,则执行表达式3;相当于是if语句的用法,但是比if更简便,更灵活。

逗号表达式

表达式1, 表达式2, 表达式3...表达式n;

 

  顺次执行每一个表达式,而这个总的表达式的值看的是最后一个表达式。

下标引用

数组名[下标];

 

  数组名[下标],可以访问数组中的对应下标的数据;

函数表达式

函数名(参数);

 

  函数调用十分简单,只需写出如上形式即可,参数可有而无;

结构体成员

.
->

 

  上面形式中,第一个是在结构体变量中可以取得到结构体的成员;第二个是在结构体指针中取得结构体成员。

表达式求值

  隐式类型转换:

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

 

//实例1
int main()
{
 char a = 0xb6;
 short b = 0xb600;
 int c = 0xb6000000;
 if(a==0xb6)
 printf("a");
 if(b==0xb600)
 printf("b");
 if(c==0xb6000000)
 printf("c");
 return 0;
}

  算数转换:

    如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作 就无法进行。下面的层次体系称为寻常算术转换:

long double
double
float
unsigned long int
long int
unsigned int
int

    如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

    警告:

float f = 3.14;
//隐式转换,会有精度丢失
int num = f;

但是算术转换要合理,要不然会有一些潜在的问题。

操作符属性

  复杂表达式的求值有三个影响的因素:

    1.操作符的优先级

    2.操作符的结合性

    3.是否控制求值顺序

操作符的优先级: