考试复杂表达式求值问题(2)

90 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情

算数转换

可能学会了整型提升的小伙伴就有所疑惑了,当一个数据超过整型呢? 当这些数据是longlong long double 等数据类型计算时该如何呢?

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

该如何转换呢? 也是像整型提升那样,都转换成整型吗? 那必须不是 我们来看看算数转换的规律

//从下往上,层次转换
 long double
 double
 float
 unsigned long int
 long int
 unsigned int
 int

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

许多操作数类型为算数类型的双目运算符会引发转化,
并以类似的方式产生结果类型。他的目的是产生一个普通类型,
同时也是运算结果的类型。这个模式称为“寻常算数转换”。

                        ——ANSI C手册

通俗来说:算数转换朝着精度更高,空间大小更大的类型进行转换。

//错误的转换
float f=3.1415;
int a=f; //隐式转换,精度丢失!

操作符的属性

在C语言操作符详解中,我已经介绍过了C语言中的所有操作符,还没看的伙伴可以点击查看操作符详解! 复杂表达式的求值有三个影响的因素。

  1. 操作符的优先级

两个相邻操作符的计算顺序取决于它们的优先级。

  1. 操作符的结合性

当两个操作符的优先级相同时,计算顺序就要看它们的结合性,结合性就是决定从左向右,还是从右向左计算。

  1. 是否控制求值顺序。

控制求值顺序就是像||&&一样会发生短路,当一个假时另一个表达式就停止计算,当一个为真时另一个表达式就停止计算!

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

操作符优先级及结合性 在这里插入图片描述 这是<<C和指针>>一书中操作符优先级表! 学习C语言的同学怎么可以没读过这本书,强烈安利大家学习!有需要电子版的伙伴可以私聊bug郭

操作符的优先级从上到下,由高到低! 表达式的求值部分由优先级决定!

问题表达式

//表达式一
a*b + c*d + e*f;

这个代码可能有多种结果 我们第一步只能确定在第一个乘和第一个加,是先计算a*b,而第三个乘不能确定是否比第一个加早,所以有多种计算方式。

//1
a*b
c*d
a*b+c*d
e*f
a*b+c*d+e*f
//2
a*b
c*d
e*f
a*b+c*d
a*b+c*d+e*f
//表达式二
c + c--;

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

//表达式三,非法表达式
int main()
{
    int i = 10;
    i = i-- - --i * ( i = -3 ) * i++ + ++i;
    printf("i = %d\n", i);
    return 0;
}

上面表达式的结果在不同编译器下结果不一样。

C和指针一书中整理了不同编译器下的结果 在这里插入图片描述

//表达式四
int fun()
{
     static int count = 1;
     return ++count;
}
int main()
{
     int answer;
     answer = fun() - fun() * fun();
     printf( "%d\n", answer);//输出多少?
     return 0;
}

这个代码有没有实际的问题? 有问题! 虽然在大多数的编译器上求得结果都是相同的。 但是上述代码answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法。函数的调用先后顺序无法通过操作符的优先级确定。 这样的表达式求值问题是压根没有意义的,如果学校再出此类问题,你就将<<C和指针>>一书甩在你老师面前!

//表达式五
#include <stdio.h>
int main()
{
    int i = 1;
    int ret = (++i) + (++i) + (++i);
    printf("%d\n", ret);
    printf("%d\n", i);
    return 0;
}
//尝试在linux 环境gcc编译器,VS2013环境下都执行,看结果。

linux环境gcc编译器结果 10 4 vs2013环境下结果 12 4 看看同样的代码产生了不同的结果,这是为什么? 简单看一下汇编代码,就可以分析清楚! 这我就不带大家研究了,如果有兴趣的伙伴可以研究一下!

这段代码中的第一个+ 在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个+ 和第三个前置++ 的先后顺序。

总结:

我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的!