C语言笔记-5:操作符详解

2021-03-13
1961字
4分钟
C

C语言笔记-5:操作符详解

操作符详解

算术操作符

+  -  *  /  %
//除法算出的是 商

int a = 5 / 2;//商2余1 - 整数除法
//将除号左右的一个数字改为小数
double b = 5.0 / 2;
printf("%lf", b);//默认打印6位小数

//取模的左右操作数必须是整数

移位操作符

作用于整数

>>  <<
int main()
{
    int a  = -1;
    //移动二进制位 - 补码的二进制位
    int b = a >> 1;//算术移位 - 通常
    //10000000000000000000000000000001 - 原码
    //11111111111111111111111111111110 - 反码
    //11111111111111111111111111111111 - 补码
    printf("%d", b);//-1
}

右移操作符

  • 算术右移

    右边丢弃,左边补原符号位

  • 逻辑右移

    右边丢弃,左边补0

左移操作符

​ 左边丢弃,右边补0

警告: 对于移位运算符,不要移动负数位,这是未定义的

位操作符

&  |   ^

二进制位

同样以补码进行运算

不创建临时变量,交换两个变量的值

溢出

int a = a + b;
int b = a - b;
int a = a - b;

不溢出

int a = a ^ b;
int b = a ^ b;
int a = a ^ b;

计算一个数字补码中1的个数

正常操作,需要不停地判断循环

//循环 /2 判断

采用按位与运算,32次

//num & 1; - 得到num的最低位
#include<stdio.h>

int main()
{
    int num = ;
    int count = 0;
    for (int i = 0;i<32;i++)
    {
        if ((num>>1)&1 == 1)
            count+=;
    }
    printf("%d",count);
    return 0;
}

精简的减少循环次数,但是不容易想到

#include<stdio.h>

int main()
{
    int num = ;
    int count = 0;
    while (num)
    {
        count++;
        num = num & (num-1);
    }
    printf("%d",count);
    return 0;
}

赋值操作符

int weight = 120;//初始化
weight = 89;//不满意赋值
double salary = 10000.0;
salary = 50000.0;

赋值操作符可以连续使用(不推荐

a = x = y+1;
//连续赋值

清晰而易于调试的写法

x = y+1;
a = x;

复合赋值符

+=

-=

*=

/=

%=

>>=

<<=

&=

|=

^=

单目操作符

只有一个操作数

!          //逻辑反操作
-          //负值
+          //正值
&          //取地址
sizeof     //操作数的类型长度(以字节为单位)
~          //对一个数的二进制位取反
--         //前置、后置--
++         //前置后置++;
*          //间接访问操作符(解引用操作符)
(类型)      //强制类型转换

数组的类型(去掉数组名

printf("%d",sizeof(int [10]))

sizeof() 后加变量名可以省略括号

#include<stdio.h>

int main()
{
    short s = 0;
    int a = 10;
    printf("%d\n",sizeof(s = a + 5));//2
    printf("%d\n",s);//0
    return 0;

表达式的结果要放在s中,只是短整型;而sizeof中的表达式不参与运算

#include<stdio.h>

int main()
{
    int a = 0;
    int x = ~a;
    printf("%d\n",x);//-1
    return 0;
}

//00000000000000000000000000000000
//11111111111111111111111111111111 - 补码
//11111111111111111111111111111110 - 反码
//10000000000000000000000000000001 - 原码
#include<stdio.h>

int main()
{
    int a = 11;
    int b = a | (1<<2);//将第三位的0变成1
    //00000000000000000000000000001011
    //00000000000000000000000000000100
    int c = b & ~((1<<2));//将第三位的1还原成0
    //00000000000000000000000000001111
    //11111111111111111111111111111011
    return 0;
}

++--的前置后置表示先加减还是先使用

关系操作符

大于小于等于不等于

逻辑操作符

&&               //逻辑与
||               //逻辑或

要与按位与/或区分开,相当于 et 和 ou

&&的左边如果是,则右边不会再计算

||的左边如果是,则右边不会再计算

EXo

#include<stdio.h>

int main()
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++;
    printf("%d %d %d %d\n",a,b,c,d);//1 2 3 4
    i = a-- && ++b && d++;
    printf("%d %d %d %d\n",a,b,c,d);//0 3 3 4
    i = a++ || ++b || d++;
    printf("%d %d %d %d\n",a,b,c,d);//1 4 3 5
    i = a++ || ++b || d++;
    printf("%d %d %d %d\n",a,b,c,d);//2 4 3 5
    return 0;
}

条件操作符

exp1 ? exp2 : exp3

逗号表达式

就是用逗号隔开的多个表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果。

int a = 1;
int b = 1;
int c = (a>b, a=b+10, a, b=a+1);
//c=?
//c = 13
if (a=b+1, c=a/2, d>0);
//真正判断的是d>0
//bofore
a = get_val();
count_val(a);
while (a>0)
{
    //{业务处理}
    a = get_val();
    count_val(a);
}

//用逗号表达式改写
while (a = get_val, count_val(a), a>0)
{
    //{业务处理}
}

下标引用、函数调用和结构成员

  1. [] ,下标引用

  2. () ,操作数是函数名,以及参数

  3. 访问一个结构的成员

    . 结构体.成员名

    -> 结构体指针->成员名

    //创建了一个结构体类型 - struct Stu
    struct Stu
    {
        //成员变量
        char name[20];
        int age;
        char id[20];
    }
    
    int main()
    {
        int a = 10;
        //使用struct Stu这个类型创建了一个学生对象s1,并初始化
        struct Stu s1={"张三", 20, "20241011"};
        struct Stu* ps = &s1;
        printf("%s\n",s1.name);
        struct Stu* ps = &s1;
        printf("%s\n",(*ps).name);
        printf("%s\n",ps->name);//指针指向了对象
        return 0;
    }
    

表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性所决定的。

同样,在这个过程中可能会有类型的转换

隐式类型转换

整型提升

为了获取整型类型的精度,表达式中的字符短整型在使用之前被替换为普通整型,这种转换称为整型提升

#include<stdio.h>

int main()
{
    char a = 3;
    //00000000000000000000000000000011
    //00000011 - a
    char b = 127;
    //00000000000000000000000001111111
    //01111111 - b
    char c = a+b;
    //a和b相加
    //00000000000000000000000000000011
    //00000000000000000000000001111111
    //00000000000000000000000010000010
    printf("%d\n",c);//-126
    //10000010 - c
    //11111111111111111111111110000010 - 补码
    //00000000000000000000000001111110 - 原码
    return 0;
}

如何进行整型提升

按照变量的数据类型的符号位来进行提升

高位补充符号位

无符号数直接补0

实例1

#include<stdio.h>

int main()
{
    char a = 0xb6;
    short b = 0xb600;
    int c = 0xb6000000;
    a==0xb6?printf("a"):1;
    b==0xb600?printf("b"):1;
    c==0xb6000000?printf("c"):1;
    return 0;
}
//只会打印c
//报出警告 warning: comparison is always false due to limited range of data type

实例2

#include<stdio.h>

int main()
{
    char c =1;
    printf("%u\n",sizeof(c));//1
    printf("%u\n",sizeof(+c));//4
    printf("%u\n",sizeof(!c));//1
    return 0;
}

c只要参与了表达式运算,就会发生整型提升

算术转换

两个不同类型的数据进行计算时,要合理转换

操作符的属性

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

  1. 操作符的优先级

  2. 操作符的结合性

    从左到右还是从右到左

  3. 是否控制求值顺序

    &&, ||, ?:

一些问题表达式

  1. a*b+c*d+e*f

不能保证第三个*,一定比第一个+早执行

  1. c+ --c

的优先级高于+

我们无法得知+操作符的左操作数的获取是在右操作数之前还是之后,有歧义

  1. 非法表达式-1

    int main()
    {
        int i = 10;
        i = i-- - --i * ( i = -3 )*i++ + ++i; 
        printf("%d\n",i);
        return 0;
    }
    

    这个表达式在不同的编译器中有不同的输出结果

  2. 非法表达式-2

    int fun()
    {
        static int count = 1;
        return ++count;
    }
    
    int main()
    {
        int answer;
        answer = fun() - fun() * fun();
        printf("%d\n", answer);
        return 0;
    }
    

    不能确定调用的时间

Avatar
Zhang.Mathias 巧者劳而智者忧,无能者无所求,饱食而遨游,泛若不系之舟