• 操作符详解(3)


    9. 条件操作符

    条件操作符也叫三目操作符

    因为有三个操作数

    exp1 ? exp2 : exp3
    
    • 1

    如果表达式1为真,计算表达式2,不计算表达式3,表达式2的结果是整个表达式的结果。

    如果表达式1为假,计算表达式3,不计算表达式2,表达式3的结果是整个表达式的结果。

    练习:

    1.
    if (a > 5)b = 3;
    else
    b = -3;
    转换成条件表达式,是什么样?
    2.使用条件表达式实现找两个数中较大值。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1

    #include 
    int main()
    {
    	int a = 3;
    	int b = 0;
    	b = ((a > 5) ? 3 : -3);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2

    #include 
    int main()
    {
    	int a = 3;
    	int b = 0;
    	int max = 0;
    	max = (a > b ? a : b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    10.逗号表达式

    exp1, exp2, exp3, …expN
    
    • 1

    逗号表达式,就是用逗号隔开的多个表达式。

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

    一定要从左向右依次执行,因为左边的运算可能影响右边某些值的变化。

    代码1

    int a = 1;
    int b = 2;
    int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
    c是多少?
    
    • 1
    • 2
    • 3
    • 4

    a>b表达式为假,所以这个表达式的值为0

    a=b+10 a的值变为12

    b=a+1 b的值变为13

    所以整个表达式的值也为13

    代码2

    if (a =b + 1, c=a / 2, d > 0)
    
    • 1

    改变了a c的值 ,最后判断d>0表达式真假,为真整个表达式值为1,if语句执行

    表达式为假,整个表达式值为0,if语句不执行。

    代码3

    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
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    由于代码3上半部分的代码有点冗余,所以我们也可以通过逗号表达式进行简化。

    当执行完while括号内的逗号表达式后,while真假进行判断是否进入循环。

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

    1.[ ] 下标引用操作符

    下标引用操作符不是定义数组时使用的方括号

    int main()
    {
    	int arr[10];  //这里的方括号不是下标引用操作符
    	arr[5] = 5;  //这里通过下标引用操作符访问数组下标为5的空间
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    下标引用操作符的操作数:一个数组名 + 一个索引值

     int arr[10];//创建数组
     arr[9] = 10;//实用下标引用操作符。
     [ ]的两个操作数是arr和9
    
    • 1
    • 2
    • 3

    2.( ) 函数调用操作符

    void test(int x, int y)
    {
    
    }
    void test2()
    {
    
    }
    int main()
    {
    	test(3, 4);  //圆括号就是函数调用操作符
    	//三个操作数:函数名test,3,4
    	test2();
    	//一个操作数:函数名test2
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

    3.访问一个结构的成员

    . 结构体.成员名
    -> 结构体指针->成员名
    
    • 1
    • 2
    #include 
    struct book  //变量类型struct book
    {
    	char name[20];  //结构体成员名name
    	int price;  //结构体成员名price
    };
    int main()
    {
    	struct book sb = { "鹏哥C语言",55 };
    	printf("%s %d\n", sb.name, sb.price);  //结构体变量.结构体成员名
    	struct book* ps = &sb;
    	printf("%s %d\n", (*ps).name, (*ps).price);
    	printf("%s %d\n", ps->name, ps->price);  //结构体指针->结构体成员名
    	return 0;
    }
    //-----------------------
    //编译器运行结果为
    //鹏哥C语言 55
    //鹏哥C语言 55
    //鹏哥C语言 55
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    (*ps).name中括号不能去掉,去掉以后.的优先级更高,直接ps.price再进行解引用。
    
    • 1

    补充:
    vscode—编辑器

    可以装各种的插件,构建Java python c/c++的环境

    但是更适合于前端。

    初学者不推荐vscode,配置成本过高,一堆问题出现。

    12.表达式求值

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

    同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

    12.1隐式类型转换

    C的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。

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

    char类型和short类型需要转化为int类型才能进行运算。

    代码示例:

    int main()
    {
    	char a = 5;
    	char b = 126;
    	char c = a + b;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    char类型的a和b先转化为int类型计算完后,再发生截断,存储到c中。

    整型提升的意义

    表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
    
    因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
    
    通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    //实例1
    char a,b,c;
    ...
    a = b + c;
    
    • 1
    • 2
    • 3
    • 4

    b和c的值被提升为普通整型,然后再执行加法运算。

    加法运算完成之后,结果将被截断,然后再存储于a中。

    如何进行整体提升呢?

    整形提升是按照变量的数据类型的符号位来提升的

    #include 
    int main()
    {
    	char a = 5;
    	//5的补码00000000 00000000 00000000 00000101
    	//截断存储在a中的数据      00000000 00000101
    	char b = 126;
      //126的补码00000000 00000000 00000000 01111110
      //截断存储在b中的数据        00000000 01111110
    	char c = a + b;
    	printf("%d", c);
    	return 0;
    }
    //打印结果为-125
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    整型提升存储在char中的最高位是0就补0,是1就补1
    整型提升之前
    00000101 -a
    01111110 -b
    整型提升后
    00000000 00000000 00000000 00000101 a
    00000000 00000000 00000000 01111110 b
    提升后进行相加
    00000000 00000000 00000000 10000011
    再进行截断存储到c中
    10000011
    当c需要以%d的形式进行打印有符号十进制数字的时候,需要进行整型提升
    11111111 11111111 11111111 10000011 整型提升后的补码
    11111111 11111111 11111111 10000010 整型提升后的反码
    10000000 00000000 00000000 01111101 整型提升后的原码
    对应的值为-125

    //负数的整形提升
    char c1 = -1;
    变量c1的二进制位(补码)中只有8个比特位:
    1111111
    因为 char 为有符号的 char
    所以整形提升的时候,高位补充符号位,即为1
    提升之后的结果是:
    11111111111111111111111111111111
    //正数的整形提升
    char c2 = 1;
    变量c2的二进制位(补码)中只有8个比特位:
    00000001
    因为 char 为有符号的 char
    所以整形提升的时候,高位补充符号位,即为0
    提升之后的结果是:
    00000000000000000000000000000001
    //无符号整形提升,高位补0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    整形提升的例子:

    //实例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; 
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    实例1中的a,b要进行整形提升,但是c不需要整形提升

    a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表

    达式 c==0xb6000000 的结果是真.

    所程序输出的结果是:

    c
    
    • 1
    //实例2
    #include 
    int main()
    {
    	char c = 1;
    	printf("%u\n", sizeof(c));
    	printf("%u\n", sizeof(c+1));
    	printf("%u\n", sizeof(+c));
    	printf("%u\n", sizeof(-c));
    	return 0;
    }
    //---------------------
    //编译器运行结果为
    //1
    //4
    //4
    //4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    实例2中的,c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字

    节.

    表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节

    12.2算术转换

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

    long double
    double
    float
    unsigned long int
    long int
    unsigned int
    int
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    警告:

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

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

    12.3操作符的属性

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

    1. 操作符的优先级

    2. 操作符的结合性

    3. 是否控制求值顺序。

    两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

    一些问题表达式

    //表达式的求值部分由操作符的优先级决定。
    //表达式1 a*b + c*d + e*f
    
    • 1
    • 2

    注释:代码1在计算的时候,由于*比+的优先级高,只能保证,的计算是比+早,但是优先级并不能决定第三个比第一个+早执行。

    所以表达式的计算机顺序就可能是:

    a*b 
    c*d 
    a*b + c*d 
    e*f 
    a*b + c*d + e*f
    或者:
    a*b 
    c*d 
    e*f 
    a*b + c*d 
    a*b + c*d + e*f
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    //表达式2 c + --c;
    
    • 1

    注释:同上,操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。

    //代码3-非法表达式
    int main()
    {
     int i = 10;
     i = i-- - --i * ( i = -3 ) * i++ + ++i;
     printf("i = %d\n", i);
     return 0; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    表达式3在不同编译器中测试有不同结果。

    //代码4
    int fun()
    {
    static int count = 1;
    return ++count; }
    int main()
    {
    int answer;
    answer = fun() - fun() * fun();
    printf( "%d\n", answer);//输出多少?
    return 0; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这个代码有没有实际的问题?

    有问题!

    虽然在大多数的编译器上求得结果都是相同的。

    但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法。

    函数的调用先后顺序无法通过操作符的优先级确定。

    //代码5
    #include 
    int main()
    {
     int i = 1;
     int ret = (++i) + (++i) + (++i);
     printf("%d\n", ret);
     printf("%d\n", i);
     return 0; }
    //尝试在linux 环境gcc编译器,VS2013环境下都执行,看结果。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    当相同的代码在不同的编译器下运行出不同的结果,那么这段程序存在错误。

  • 相关阅读:
    最强分布式事务框架怎么炼成的?
    SD卡格式化怎么恢复?
    找工作小项目:day16-重构核心库、使用智能指针(1)
    Dubbo Admin修改注册中心为Nacos 以及Nacos整合Dubbo
    mvc core基于Asp Net的印刷网站
    通过对抽象模型和概念模型的整合,细化项目整体流程
    设计模式(一):面向对象基础、单一职责原则、开放封闭原则和依赖反转原则
    【postgresql】查看数据中表的信息
    电脑重装系统
    工控上位机程序为什么只能用C语言?
  • 原文地址:https://blog.csdn.net/m0_71545436/article/details/127528423