2021-09-30operator(操作符)- 重置版

移位操作符

移位操作符 适用于 整形数据,只能 移动 正整数位,移动负整数是错误的写法。

<< 左移操作符

#include<stdio.h>
int main()
{
//对于整数的二进制有三种表达形式:原码,反码,补码

    //正整数 原码 反码 反码都一样
    int a = 5; //存放的的是补码,printf打印的是原码
//    00000000000000000000000000000101  原码
//    00000000000000000000000000000101  反码
//    00000000000000000000000000000101  补码
    int c = -1;
//    10000000000000000000000000000001 //原码
//    11111111111111111111111111111110 //反码
//    11111111111111111111111111111111 //补码
     原码 - 直接按照数字的正负写出二进制序列
     反码 - 原码的符号位不变,其他位 按位取反
     补码 - 反码 加 1

              //整数
    int b = a << 1;//左移操作符:左边丢弃,右边补零
    printf("a = %d\n",a);// 5
     移位前,
    //00000000000000000000000000000101 //补码
    
    //移位操作符:移动的是二进制位
   移位后, a 不变,因为移位后,并没有将移位后的值,赋给 a,而是赋给了 b
    //00000000000000000000000000001010//移位后 
    printf("b = %d\n",b);// 10

 
    int d = c << 1;
//    11111111111111111111111111111111  -1 的 补码
//  11111111111111111111111111111110 - 移位后
    printf("d = %d\n", d);//打印的是原码  -2
//  11111111111111111111111111111110  补码
//  11111111111111111111111111111101 反码 = 补码 - 1
//  10000000000000000000000000000010 原码 == 反码符号位不变,其余按位取反
    return 0;
}

>>右移操作符 :右移有2种方式

1.逻辑移位 右边丢弃,左边补零。

2.算术移位 右边丢弃,左边用原该值的符号位填充。(最常用)
#include<stdio.h>
int main()
{
    int a = 5;//因为 a = 5 是整数,即 原反补 三码相同
    int b = a >> 1;
//    00000000000000000000000000000101 //原码
//    00000000000000000000000000000010 //移位后的原码()
   printf("b = %d\n",b);// 2
    int c = -1;//根据 -1 的值写出原码 - 反码 - 补码
// 10000000000000000000000000000001 //原码
// 11111111111111111111111111111110 //反码
// 11111111111111111111111111111111 //补码
       int d = c >> 1;
// 11111111111111111111111111111111 //算术移位:右边丢弃,左边用原该值的符号位填充。值不变   -1

// 01111111111111111111111111111111 //逻辑移位: 右边丢弃,左边补零。
//原码 00000000000000000000000000000001  值为 1
//如果使用逻辑移位,移动2位的话, 想象一下,值和符号都会发生变化

    printf("d = %d\n", d); //当前电脑 vs2013 运用的是算术右移
      //打印的是原码 值为 -1
  //  10000000000000000000000000000001 原码 
    return 0;
}

位操作符:

下面的为操纵符 都是 按二进制位(补码)进行逻辑计算
 & 按位与   
 | 按位或
^ 按位异或
& 按位与 (二进制位(补码)进行逻辑计算)
双1出1,有0出0, 【C = A&B,假设 A,B 都是 1,C才能是 1,如果 A,B中有一个 为 0,即 C 等于 0 】
#include<stdio.h>
int main()
{
    int a = 3;// 3 为正整数
    //00000000000000000000000000000011 //原码 = 反码 = 补码
    int b = -2;
    //10000000000000000000000000000010 //原码
    //11111111111111111111111111111101 //反码
    //11111111111111111111111111111110 //补码
    int c = a & b;
    //00000000000000000000000000000011 // a 补码
    //11111111111111111111111111111110 // b 补码
    //00000000000000000000000000000010 // c 的补码
    
    printf("c = %d\n",c);
       //符号位为0;正整数 所以 三码相同
    //00000000000000000000000000000010
 %d 说明我们打印的是 有 符号数
 %u 说明我们打印的是 无 符号数
 所以最终结果为  2
    return 0;
}

| 按位或 (二进制位(补码)进行逻辑计算)
有1出1,双0出0【假设 C = A | B, 只有 A,B 全部为 0,C才等于 0,只要 A,B中有一个为1,C 就等于 1】
#include<stdio.h>
int main()
{
    int a = 3;
    //00000000000000000000000000000011  三码相同,整数在内存中存的是补码
    int b = -2;
    //10000000000000000000000000000010   原码
    //11111111111111111111111111111101   反码 == 原码符号位不变,其他位按位取反
    //11111111111111111111111111111110   补码 == 反码 + 1
    int c = a | b;
    //00000000000000000000000000000011 // a 补码
    //11111111111111111111111111111110 // b 补码
    //11111111111111111111111111111111 // c 的补码
    
    printf("c = %d\n", c);// -1
    打印是打印原码 
    11111111111111111111111111111111 补码
    10000000000000000000000000000000 反码 == 补码 符号位不变,其他按位取反
    10000000000000000000000000000001 原码 == 反码 + 1
    最高位是符号位 为 1, 代表为负数,二进制最低位 为 1,转换成 十进制数 2^0 == 1
    结合符号位 ,这个整数为 -1 
    return 0;
}

^ 按位异或 相异出1,相同出0
#include<stdio.h>
int main()
{
    int a = 3;
    int b = -2;
    10000000000000000000000000000010 原码
    11111111111111111111111111111101 补码
    11111111111111111111111111111110 补码
    int c = a ^ b;
    //00000000000000000000000000000011  a 补码
    //11111111111111111111111111111110  b 补码
    //11111111111111111111111111111101  c 的补码
    //10000000000000000000000000000010  c 的反码
    //10000000000000000000000000000011  c 的原码
    printf("c = %d\n", c);//  -3
    return 0;
}


不创建变量,交换两个变量 ( 0^b=b)&&(a异或 a 等于 0,相同为 0;b异或 b 等于 0,相同为 0)

#include<stdio.h>
int main()
{
    int a = 0;
    int b = 15;
    a = a^b;
    b = a^b;// a^b^b 》 b^b == 0[相同为 0],0^a == a 
    // b = a;
    
    a = b^a;// a^a^b 同理 a^a = 0,0^b 就是 b
    // a == b
    printf("%d\n", a);
    printf("%d\n", b);
    return 0;
}

连续赋值

#include<stdio.h>
int main()
{
    int a = 10;
    int b = 10;
    int x = 0;
    a = x = b + 1;//(建议)x=b+1;  a=x; 
    先计算b+1;赋给x,再赋给a                  
    printf("%d", a);
    return 0;
}



#include<stdio.h>
int main()
{
    int a = 10;
    00000000 00000000 00000000 00001010 三吗相同
    
    a = a >> 1;// a == 5
    00000000 00000000 00000000 00000101 (编译器一般都是 算术逻辑,移动一位后,最高位空出来了,根据原先的符号位。进行补位,这里原先的符号位为0,所以空出的位置 补 0)
  
  
    a >>= 1; // a == 2
    00000000 00000000 00000000 00000010
   
   
    a = a + 10;
    // a = 2 + 10 = 12
    
    a += 10;// a = 12 + 10 = 22
    return 0;
}


单目操作符(unary operator)

! —— 逻辑反操作

#include<stdio.h>
int main()
{
    int a = 5;
    int b = !a;
    printf("%d\n", b);// a为一个非零的数,即为真,!a == 0 ,! 操作符 的 作用:把真变为假,反之 如果 a = 0    !a== 1
    //所以 b == 0.,打印 0
    
    if (a != 0)//a不为假,打印hehe
    {
        printf("hehe\n");// a = 5,a为真,打印 hehe
    }
    if (!a)//a为假,打印haha
    {
        printf("haha\n");
    }
    return 0;
}

&取地址 *解引用符 sizeof:

程序一:
#include<stdio.h>
#include<string.h>
int main()
{

    char array[10] = "abc";// 10个字节的内存
    printf("%d\n",sizeof(array));// 10
    printf("%d\n", sizeof array);//两者等价,因为sizeof操作符不是函数,所以可去括号
                                 //如果括号里是类型:sizeof(int),此处括号不可省略
                   //计算变量或者类型创建变量的内存大小,单位字节,和内存中存放什么没有关系
                   
    printf("%d\n", strlen(array)); // 3
                 // strlen(arr) 遇到'\0',就停止计算(计算的元素个数不包含 '\0')
                 // 且不能省略().

    int arr[10] = { 0 };
    // arr    数组名 通常 表达的意思 是 数组首元素地址

           只有两种特殊情况
     &arr 和 sizeof(arr) 中的 arr 都是代表整个数组【数组的地址】
    // &arr[0]//数组首元素地址,sizeof(arr[0]) 首元素的字节大小
    // &arr[9]//数组第10个元素地址,这里就不解释了,数组是 一群相同类型数据的集合
    
    int a = 10;     
    int* p = &a;
    *p = 20;//*p放在左边代表的是用来存放数值的空间,
    printf("%d\n", a);// 20
    int b = *p;// *p放在右边代表的是存储的空间里存储的数值
    printf("%d\n",b);//  20
    return 0;
}
程序二:
#include<stdio.h>
int main()
{
    int a = 5;
    short s = 10;
    printf("%d\n", sizeof(s = a + 1));// 2  
    s是短整型,a+1=6,把这个整形放进短整型,放不下,发生截断
    最终的结果,归s负责,short类型,算变量或者类型创建变量的内存大小,和内存中存放什么没有关系
    sizeof计算的类型的字节大小,并不涉及实际计算,short 为短整型,字节大小为 2,即输出结构为 2
    另外 它计算玩 short 之后,后面 a + 1 ,它管都不管。所以 a+1 根本没机会运行
    s 还是等于 10

    printf("%d\n",s);// 10  sizeof不参与计算,直接计算出sizeof(short)拿走了,所以后面 a+1;根本就没机会运行,所以s还是10.
    return 0;
}


~ 按位(二进制位)取反(包括符号位)

程序一:
#include<stdio.h>
int main()
{
    int a = 0;
    //00000000000000000000000000000000
    int b = ~a;
    //11111111111111111111111111111111 //补码
    printf("%d",b);// 原码= (补码 - 1)取反 (不包括符号位)
    //10000000000000000000000000000001
    // 值为 -1
    return 0;
}
由此就可以 看出 ~ 符号的特殊性,它会把 二进制符号位也一起取反

程序二:
#include<stdio.h>
int main()
{
    int a = 13;
    //00000000000000000000000000001101 // a为正整数 三码一样,这是补码


    a |= (1 << 1);//00000000000000000000000000001111 //  a 与 1<< 1 逻辑或之后,把 1101中的0改成了 1 ,即1111
    // 1的补码
    //00000000000000000000000000000001 
    //1 << 1(1的补码向左移动一位)
    //即:00000000000000000000000000000010 

    printf("%d\n", a);// 值为 15
    // 00000000000000000000000000001111

    a &= (~(1 << 1));
    //00000000000000000000000000000010  1 << 1
    按位取反
    // 11111111111111111111111111111101 
     &
    // 00000000000000000000000000001111//  00000000000000000000000000001101
    printf("%d\n", a); //13

    
    return 0;
}

/前置加加 / 减减 ; 后置加加 / 减减

程序一:
#include<stdio.h>
int main()
{
    int a = 10;
    int b = a++;//后置加加,先赋值,后加加(把 a = 10的时候 ,赋给 b【b == 10】,然后 a 开始自增加一【a == 11】)
    printf("%d\n",b);//10
    printf("%d\n", a);//11
    return 0;
}

程序二:
#include<stdio.h>
int main()
{
    int a = 10;
    int b = ++a;//前置加加,先加加,后赋值(a先自增加一【a == 11】,然后再将其 赋予 b【b == 11】)
    printf("%d\n",b);//11
    printf("%d\n", a);//11
    return 0;
}

程序三:
#include<stdio.h>
int main()
{
    int a = 10;
    int b = a--;//后置减减,先赋值,后减减(先把 a == 10 的时候,赋给 b【b == 10】,然后 a 开始 自减减一【a == 9】)
    printf("%d\n", b);//10
    printf("%d\n", a);//9
    return 0;
}
程序四:
#include<stdio.h>
int main()
{
    int a = 10;
    int b = --a;//前置减减,先减减,后赋值(先 a 自减减一【a == 9】,再将其值 赋给 b【b == 9】)
    printf("%d\n", b);//9
    printf("%d\n", a);//9
    return 0;
}

() 强制转换符

#include<stdio.h>
int main()
{
    int a = (int)3.14;// 把 3.14 强制类型转换,等于 3,【取整数部分】
    printf("%d\n",a);// 3
    return 0;
}

&& 逻辑与 只关注真假

#include<stdio.h>
int main()
{
    int a = 1;
    int b = 2;
    int c=a&&b;
    printf("%d\n",c);// a 和 b 都是非零,即c为真 c == 1,如果 a,b中有一个为 0(任何一个为假),则 c 为假,c == 1
    return 0;
}

|| 逻辑或,有1出1,双零出零

#include<stdio.h>
int main()
{
    int a = 1;
    int b = 0;
    int c = a||b;
    printf("%d\n", c);// a 是非零,所以c为真 c == 1。(a,b中只要有一个正整数【只要有一个真】,c 就为真,即 c == 1 )
    return 0;
}

>= <=

#include<stdio.h>
int main()
{
    int age = 0;
    scanf("%d",&age);
    if ((age >= 10) && (age <= 30))
    {
        ;// 只有满足上述条件(age 必须大于等于10,小于等于30,两者缺一 不可),才执行 该语句
    }
    if ((age >= 10) || (age <= 30))
    {
        ;// 只有满足上述条件(age 必须大于等于10 或 小于等于30,两者 择其一 即可),才执行 该语句
    }
    return 0;
}

360 笔试题

程序一:

#include<stdio.h>
int main()
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++; // a++ 先调用再加加,刚开始的时候,a=0;所以为假,与任何数相与都为假。 所以 i == 0,另外 ++b 和 d++ 没有执行,因为 && 与逻辑操作符,只关注真假,而且只有 有一个假,那么假,后面的数据它就不在管了,因为 && 符号  有一个为假,整体就为假。

                       用完之后 a自增加一, a 等于 1,其它都不变

    printf("a = %d b = %d c = %d d = %d\n",a,b,c,d);//即输出位 1,2,3,4
    return 0;
}

程序二:

#include<stdio.h>
int main()
{
    int i = 0, a = 1, b = 2, c = 3, d = 4;
    i = a++ && ++b && d++; 
    注意 此时 a=1;是为真, ++b先加后赋值,即b==3, d++先加后赋值,此时d=4,为真,三个条件都为真,所以 i 为真(&&的规则),故 i == 1
    
    用完之后 a=2(a在调用完后,自增加一【a == 2】),b= 3,c=3,d=5(d在调用完后,自增加一【b == 5】)
    printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);//即输出位 2335
    return 0;
}

程序三:

#include<stdio.h>
int main()
{
    int i = 0, a = 1, b = 2, c = 3, d = 4;
    i = a++ || ++b || d++; //因为 a=1;是为真,|| 或逻辑操作符 只要一个为真,就是真,所以后面的  || ++b || d++   都不执行。
    
    所以用完之后 a=2(a在被调用完之后,自增加一【a == 2】),b= 3,c=3,d=4
    printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);//即输出位 2234
    return 0;
}

程序四:

#include<stdio.h>
int main()
{
    int i = 0, a = 0, b = 2, c = 3, d = 4;
    i = a++ || ++b || d++; // 注意!此时a=0;是为假;++b,先加后赋,b == 3, 此时满足了 或逻辑只要一个为真,整体就是真 的条件,所以后面的 || d++ 都不执行
    //用完之后 a=1(a在被调用完之后,自增加一【a == 1】),b= 3,c=3,d=4
    printf("a = %d b = %d c = %d d = %d\n", a, b, c, d);//即输出位 1334
    return 0;
}

条件操作符 ()?()😦) 又称 三目操作符

#include<stdio.h>
int main()
{
    int a = 10;
    int b = 20;
    int c = (a > b) ? (a) : (b);
     三目操作符表达式 (a > b) ? (a) : (b) 的意思是  如果 a > b为真, 就返回a,否则返回 b
    printf("%d\n",c);//a > b 为假,所以返回 b
    return 0;
}

逗号表达式

程序一:

#include<stdio.h>
int main()
{
    int a = 1;
    int b = 2;
    int c = (a > b, a = b + 10, a, b = a + 1/* 13 */);
    
    a > b      :只是执行一下(只是判断一下)
    a = b + 10 :此时  a == 12 
    a          :意思就是 a == 12
    b = a + 1  :此时 b == 13 

逗号表达式 从左往右计算,以 最右边的式子 的 结果 为 整个式子的结果(b == 13 , 即 c == 13printf("%d\n",c);// 13
    return 0;
}

程序二:

/#include<stdio.h>
int main()
{
    int a = get_val();
    count_val(a);
    while (a > 0)//while (int a = get_val(),count_val(a);a > 0) //逗号表达式可以运用到循环中,从左往右计算
    {
        ;
    }
    return 0;
}

下标引用操作符 [ ],函数调用操作符 (),结构访问操作符 . 和 ->

下标引用操作符

#include<stdio.h>
int main()
{
    int  arr[] = { 1, 2, 3, 4, 5 };
    int i = 0;
    //arr[4]// 代表的是数组中第五个元素 5,等价于 *(arr+4)== *(4+arr)   和  4[arr],说明这些式子 支持 交换律
      []是一个操作符,而 4 和 arr 是操作数

     *(arr+4) == *(4+arr); 意思是用 数组 arr 的 首元素地址 加上 4 ,找到 下标为4 的元素(第五个元素)
        该式子支持交换律
    for (i = 0; i < 5; i++)
    {
        printf("%p\n",&arr[i]);//输出的数列arr中 下标为 i 元素的地址,(第 i - 1 个元素的地址)
        printf("%p\n",arr+i);//与上式子完全等价,数组名 arr,没有和 & 和 sizeof 联合使用,所以表达的是 首元素地址,想得到后面的元素的地址很简单,加上 i(注意 i 的类型与 数组 元素类型相同,+ i,等于跳过 i 个元素【举个例子:arr + 1,arr代表首元素地址,对其加一,等于 跳过 一个 int 类型的内存大小,而数组arr的元素的类型就 int,就是说跳过 首元素,指向下一个元素的起始地址】)
    }
    return 0;
}

函数调用操作符 ()

 能 接受 一个 或者 多个 操作数;第一个操作数是函数名,其余操作数就是专递函数的参数

#include<stdio.h>
#include<string.h>

void test()
{
    printf("hehe\n");
}

int main()
{
    char  arr[] = "hello world1";
    strlen(arr);// 这里的小圆括号,是函数调用操作符,不可省略(strlen arr 【error - 错误写法】)
    //strlen  函数的返回值是 size_t == unsigned int 无符号整形

    printf("%u\n",strlen(arr));//   %u,表示打印无符号整形,%d 表示打印有符号整形
    printf("%u\n", strlen("hello"));//函数调用操作符() ,不可省略


    test();//函数调用操作符() ,不可省略.即使不传参,也是一样。

    return 0;
}

结构访问操作符 . 和 ->

例:
  结构体变量.成员名   
  结构体指针 -> 成员名    -> 指针型 
#include<stdio.h>

//自定义类型
struct book
{
    char name[20];
    float price;
    char id[20];
};

void print(struct book* pb)
{
    //常用后者表示
    printf("%s\n", pb->name);//printf("%s\n", *(pb).name);
    printf("%f\n", pb->price);//printf("%f\n", *(pb).price);
    printf("%s\n", pb->id);//printf("%s\n", *(pb).id);
}

int main()
{
    struct book b = { "z语言",48.5f, "ab12345" };

    printf("%s\n",b.name);

    strcpy(b.name,"c");//改变 或 赋值 数据结构,需要通过 strcpy 函数
    //因为数组名是地址,数据结构需要放在储存空间里
    
    b.price = 50; // 变量可以直接赋值

    print(&b);// 将结构体变量的地址

    printf("%s\n", b.name);
    printf("%f\n", b.price);
    printf("%s\n", b.id);
    return 0;
}

表达式求值的顺序

表达式求值的顺序 的  一部分 由 操作符的优先级 和 结合性 共同决定的。
同样,有些 表达式 的 操作数 在 求值的过程中 可能 需要 转换为其他类型。

隐式类型转换 :偷偷的进行类型转换

c的整形算术运算 总是 至少 以缺省整形类型的精度来进行的(缺省 的意思是 系统默认状态,就是说系统总是 以 int 类型 的 精度 来进行运算的) 
为了获得这个精度,表达式中 的 字符 和 短整型操作数 在使用之前 被转换为普通整形,这种转换 称为 整形提升。

参考程序:

#include<stdio.h>
int main()
{
    char a = 3;//char 1 byte 只能存放 8 bit
    000000000000000000000000 00000011 // 3 的 补码 。 a 是正整数 三码相同 
    00000011 //数据截断(最低位)后的数据,这就是存入a的数据
    char b = 127;
    //000000000000000000000000 01111111 // 127的二进制位
    //01111111 这是真正存入b的二进制位数据

     char c = a + b;
    // b 和 a 想要进行运算,就必须提升为普通整形,再进行加法运算,
    //加法运算完以后,结果将被截断(将整形数据存入字符类型数据中),然后存储于c中
   
    // a 和 b 如何相加   表达式中的 字符 和 短整型操作数 在使用之前 被转换为普通整形,这种转换称为 整形提升。
    //00000011     整形提升 是 按照 变量的数据类型 的 符号位 来提升的。
    //01111111     因为截断后的数据第一位是0,即为正,根据变量类型的符号位,来进行整形提升
                    //  补0,直至 32 bit(4 byte)
                   注意 无符号整形 整形提升, 直接补0

             整形提升后
    //000000000000000000000000 00000011   a
    //000000000000000000000000 01111111    b
    //000000000000000000000000 10000010 // 加法运算完后,a+b
    再进行截断 10000010(补码) 这就是放进c的二进制位

    printf("%d\n",c);// 打印原码 :100000000000000000000000 01111110  -126
    因为要 打印,所以又要进行整形提升
    111111111111111111111111 10000010(补码)  //整形提升:按照变量的数据类型的符号位(10000010)来提升的,所以补1,
    100000000000000000000000 01111101 反码
    100000000000000000000000 01111110 原码
    

    return 0;
}

整形提升的意义:

表达式 的 整形运算 要在 CPU 的 相应运算器件内执行,CPU内整形运算器(ALU) 的 操作数 的 字节长度 一般 就是 int 的字节长度,同时也是 CPU 通用 的 寄存器的长度。

因此,即使 两个char类型 的 相加,在 CPU执行时 实际上 也要 先转换为 CPU 内 整形操作数 的 标准长度。

通过CPU(general-purpose CPU)是 难以 直接实现 两个8字节 直接相加运算(虽然 机器指令中 可能有 这种字节 相加的指令)。
所以, 表达式中 各种长度 可能小于 int 长度 的 整型值,都必须先转换为 int 或 unsigned int ,然后才能送入CPU执行运算


案例 1
#include<stdio.h>
int main()
{
    char a = 0xb6;// 0xb6 二进制码 1011 0110 存入 a ;然后 0xb6 == a要进行比较,所以 a 要整形提升,按照变量的数据类型的符号位,即 1。所以补 1
                  //111111111111111111111111 1011 0110 //整形提升后,很明显 a 与 0xb6 不相等,所以不打印 a
    short b = 0xb6000;  // 与上式子同理,所以 b 也不打印
    int c = 0xb6000000;//因为 c 本来就是整形,所以不用整形提升,即if (0xb6000000 == c)条件成立,所以可以打印 c
    if (0xb6 == a)
    {
        printf("a");
    }
    if (0xb6000 == b)
    {
        printf("b");
    }
    if (0xb6000000 == c)
    {
        printf("c");
    }
    return 0;
}

案例 2
#include<stdio.h>
int main()
{
    char c = 1;//char 1 byte
    printf("%u\n",sizeof(c)); // 1  不涉及运算
    printf("%u\n", sizeof(+c));// 4 
    涉及到运算了,要进行整形计算,先 整形提升,故32 bit,即4字节。也就说sizeof计算的是一个整形的大小 
    printf("%u\n", sizeof(!c));// 1  不涉及运算
    return 0;
}

算术转换

如果 某个操作符 的 各个操作数 属于 不同的类型,那么除非其中一个操作数的转换 为另一个操作数的类型,否则操作就无法进行。
寻常算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
如果程序中出现上面其中2个类型,下面的要向上面的进行 算术转换
 比如 出现 int 和 unsigned int, int 要转换成 unsigned int 进行运算——算术转换

操作符的属性
复杂表达式的求值有三个影响的因素
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序 
 两个相邻的操作符执行哪一个?取决于它们的优先级,如果两者的优先级相同,取决于它们的结合性。

例子:
#include<stdio.h>
int main()
{
    int a = 10;
    int b = 20;
    // 1. 优先级
    int c = a + b*3;// *(乘) 的优先级比 + 的优先级要高,所以要先计算 b*3,在计算  a + z(假设 z 是 b*3得出的值)
    printf("%d\n",c);// 20*3+10= 70
    
    // 2, 结合性
    int d = a + b + 3; // + 的结合性 LR 从左往右计算
      10 + 20 + 3 == 30 + 3 == 33
    printf("%d\n", d);// 33
    
    // 3. 是否控制求值顺序
    int e = a && b + 3;// 因为 + 比 && 的优先级要高,所以先计算 b+3,再根据 逻辑与 && 结合性 LR,如果 a 为假的话,后面的式子得出的值就不重要了,甚至可能 b+3 都不会去计算,直接为假,e == 0,这就是控制求值顺序
    
   // 逻辑或 ||  条件操作符(三目操作符)()?():()  逗号表达式【结果为最后一个表达式的结果】 , 也是一样的。
                                                          
    printf("%d\n", e); // 0
    return 0;
}

总结:

一些问题表达式  (重点: 最好能确定唯一的计算路径,如果不能,说明这个表达式就是存在问题的)

举几个例子:
代码1.
 a * b + c * d + e * f  绝对不能写出这种的表达式,如果非要写,写好()因为它的优先级最高,
                                       比如 ,我想先计算a * b ,后加上 c * d,再加 e * f 
                                      即((a * b )+ c * d )+ e * f 
代码在计算的时候由于 * 的优先级高,只能保证比 + 早,但是 优先级 并不能决定  三个 * 中 哪一个 早执行。

代码2.
c + --c; // --优先级高,先--c,再加 c
但是 + 操作符的 左操作数的 获取,在 右操作数 之前 还是 之后 求值,我们不能确定,所以结果不可预测,是有歧义的
左操作数的 获取,在 右操作数 之前, 假设 c = 1 表达式为 c(1)+--c(1)= 1 + 0 == 1
左操作数的 获取,在有 操作数 之后,假设 c = 1  表达式为 c( )+ --c(1)= 0 + 0 == 0

代码3
    非法表达式
#include<stdio.h>
int main()
{
    int i = 10;
    i = i-- - --i * (i = -3) * i++ + ++i;//error 像这种大杂烩计算表达式 是 错误写法,最好能确定唯一的计算路径。
    printf("i = %d\n", i);//在不同的编译器里,输出也不同
    //在vs2013 中输出为 4
    return 0;
}

代码 4.
#include<stdio.h>
int fun()
{
    static int count = 1;
    return ++count;
}
int main()
{
    int answer;
    answer = fun() - fun() * fun();// 错误在于,不确定这三个 fun()函数,谁先调用
    //        1       2       3  按照这个顺序 LR(从左往右的方式) 调用 2 - 3 *4 = -10

    //        2       3       1     按照这个顺序   即  3 - 4 * 2 = -5

    //        3       2       1    按照这个顺序   即     4 - 3 * 2 = -2
    printf("%d\n",answer);
    return 0;
}

本文结束

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dark And Grey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值