表达式求值

目录

表达式求值

操作符的属性

隐式类型转换

算数转换


表达式求值

操作符的属性

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

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

1.操作符的优先级

2.操作符的结合性

3.是否控制求值顺序。//&& || , ?:这四个可以控制

C语言的运算符包括单目运算符、双目运算符、三目运算符,优先级如下:

第1优先级:各种括号,如()、[]等、成员运算符 . ;
​
第2优先级:所有单目运算符,如++、--、!、~等;
​
第3优先级:乘法运算符*、除法运算符/、求余运算符%;
​
第4优先级:加法运算符+、减法运算符-;
​
第5优先级:移位运算符<<、>>;
​
第6优先级:大于运算符>、大于等于运算符>=、小于运算符<、小于等于运算符<=;
​
第7优先级:等于运算符==、不等于运算符!=;
​
第8优先级:按位与运算符&;
​
第9优先级:按位异或运算符^;
​
第10优先级:按位或运算符|;
​
第11优先级:逻辑与运算符&&;
​
第12优先级:逻辑或运算符||;
​
第13优先级:三目条件运算符 ?: ;
​
第14优先级:各种赋值运算符,如=、+=、-=、*=、/= 等;
​
第15优先级:逗号运算, 。

操作符号表:

操作符描述用法示例结果类型结合性控制求值顺序
( )聚组(表达式)与表达式同N/A
( )函数调用rexp(rexp,……)rexpL-R
[ ]下标引用rexp[rexp]lexpL-R
.访问结构成员lexp.mem_namelexpL-R
->访问结构体指针lexp->mem_namelexpL-R
++后缀自增lexp++rexpL-R
--后缀自减lexp--rexpL-R
!逻辑反!lexprexpR-L
~按位取反~lexprexpR-L
+单目表示正值+rexprexpR-L
-单目表示负值-rexprexpR-L
++前缀自增++rexprexpR-L
--前缀自减--rexprexpR-L
*间接访问*rexplexpR-L
&取地址&lexprexpR-L
sizeof取长度,字节表示sizeof rexp/(类型)rexpR-L
(类型)类型转换(类型)rexprexpR-L
*乘法rexp*rexprexpL-R
/乘法rexp/rexprexpL-R
%整数取余rexp%rexprexpL-R
+加法rexp+rexprexpL-R
-减法rexp-rexprexpL-R
<<左位移rexp<<rexprexpL-R
>>右位移rexp>>rexprexpL-R
>大于rexp>rexprexpL-R
>=大于等于rexp>=rexprexpL-R
<小于rexp<rexprexpL-R
<=小于等于rexp<=rexprexpL-R
==等于rexp==rexprexpL-R
!=不等于rexp!=rexprexpL-R
&位与rexp&rexprexpL-R
^位异或rexp^rexprexpL-R
|位或rexp|rexprexpL-R
&&逻辑与rexp&&rexprexpL-R
||逻辑或rexp||rexprexpL-R
?:条件操作符rexp?rexp:rexprexpN/A
=赋值lexp=rexprexpR-L
+=以…加lexp+=rexprexpR-L
-=以…减lexp-=rexprexpR-L
*=以…乘lexp*=rexprexpR-L
/=以…除lexp/=rexprexpR-L
%=以…取余lexp%=rexprexpR-L
<<=以…左移lexp<<=rexprexpR-L
>>=以…右移lexp>>=rexprexpR-L
&=以…与lexp&=rexprexpR-L
^=以…异或lexp^=rexprexpR-L
|=以…或lexp|=rexprexpR-L
,逗号rexp,rexprexpL-r

L-R从左向右结合

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

有些表达式仍然不能完全判断计算:a * b + b * c + c * d

判断1

c + --c = 2c - 1/2c

非法表达式判断2

int main()
{
    int i = 10;
    i = i-- - --i * ( i = -3) * i++ + ++i;
    printf("i = %d",i);
    return 0;
}//不同的编译器测出的答案值不一样
vscode答案:36

判断3

#include<stdio.h>
int fun()
{
    static int count = 1;//因为有static,所以不会重置count
    return ++count;
}
int main()
{
    int answer;
    answer = fun() - fun() * fun();//2 - 3 * 4
    printf("%d\n",answer);//输出多少
    return 0;
}
-10

在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量。

判断4

#include<stdio.h>
int main()
{
    int i = 1;
    int ret = (++i) + (++i) +(++i);
    printf("%d\n",i);
    return 0;
}
VS - 12//eax = i+1+1+1 =4; 括号里面的都过一遍以后寄存器存储到4,然后三个一样的合起来变成12
vscode - 10//第一个和第二个先计算3+3,最后一个再算为4,合起来为10
Linux gcc - 10

寄存器:eax ebx ecx edx ebp esp

寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。

隐式类型转换

C的整型算术运算总是至少以缺少整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升

整型提升的意义:

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

#include<stdio.h>
int main()
{
    char a,b,c;
    a = b + c;
    return 0;
}
//b和c的值被提升为普通整型,然后再执行加法运算。
//加法运算完成之后,结果将被截断,然后再存储于中。

整型占4个字节,字符型占1个字节

整型如何提升:

#include<stdio.h>
int main()
{
    char a = 3;
    //00000000000000000000000000000011
    char b = 127;
    //00000000000000000000000001111111
    char c = a + b;
    //00000000000000000000000010000010
    //10000010-补码 10000001-反码 11111110-原码-126
    //发现a和b都是char类型的,都没有达到一个int的大小
    //这里就会发生整型提升
    printf("%d\n",c);
    return 0;
}
-126
//负数的整形提升
char c1 =-1;
变量c1的二进制位(补码)中只有8个比特位:
11111111
因为char为有符号的char
所以整型提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为char为有符号的char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001

实例1

#include<stdio.h>
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;
}
c

实例2

#include<stdio.h>
int main()
{
    char c = 1;
    printf("%u\n",sizeof c);//1
    printf("%u\n",sizeof -c);//参与运算就会发生整型提升
    printf("%u\n",sizeof +c);
    printf("%u",sizeof !c);//编译器处理的问题 gcc环境下应该也是4
    return 0;
}
1
4
4
1
#include<stdio.h>
int main()
{
    int a = 3;
    int b = 5;
    short s = 5;
    a + b;//值属性,类型属性 int
    return 0;
}

算数转换

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

 

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。 警告:但是算术转换要合理,要不然会有一些潜在的问题。

#include<stdio.h>
int main()
{
    int a = 4;
    float b = 4.5;
    printf("%f",a+b);//a进行算数转换成float类型
    return 0;
}
  • 14
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Williamtym

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

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

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

打赏作者

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

抵扣说明:

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

余额充值