推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

表达式求值c++实现,c语言实现表达式求值

   2023-08-15 网络整理佚名1970
核心提示:语言中的表达式求值C/C语言是“基于表达式的语言”,所有的计算(包括赋值)都在表达式中完成。涉及到运算对象的求值顺序和变量修改的实现时间。许编译器采用任何求值顺序,使编译器在优化中可以根据需要调整实现表达式求值的指令序列,以得到效率更高的代码。以上关于C/C语言表达式求值的讨论就是边肖分享的全部内容。

表达式求值c++实现,c语言实现表达式求值,谈谈C/C++语言中的表达式求值

下面小编就带大家简单聊聊C/C语言中的表达式求值。 我觉得小编还不错。 我现在就分享给大家,给大家一个参考。 快来和小编一起来看看吧。

在一些讨论群中经常可以看到这样的问题:“有谁知道下面的C语句给出的N值是多少吗?”

m=1; n=毫米

一位我不认识的朋友最近给我发电子邮件询问为什么在某些 C 系统上,以下表达式会打印两个 4,而不是 4 和 5:

a=4; 库特aa

左组合运算不需要C吗? 是C书有问题,还是这个系统的实现有问题?

注:操作a=4; 库特aa

例如,在 c 6.0 中,您得到 4 和 4; 在 c 6.0 中你得到 4 和 5。

哪个是对的? 详细请看下面的分析!

要理解这一点,需要理解一个问题:如果程序中某个变量被修改(通过赋值、自增/自减操作等),什么时候可以从该变量中获取新值? 有人可能会说:“有什么问题啊!我修改了变量,然后从这个变量中取出值,当然得到了修改后的值!” 事情没那么简单。

C/C语言是一种“基于表达式的语言”,所有的计算(包括赋值)都是在表达式中完成的。 “x=1;” 表达式“x=1”后跟分号表示语句结束。 要理解程序的含义,首先要理解表达式的含义,即: 1)表达式所确定的计算过程; 2)其对环境的影响(环境可以视为当时所有可用的变量)。 如果一个表达式(或子表达式)只计算一个值而不改变环境,我们说它是透明引用的,并且这个表达式对其他计算没有影响(不改变计算的环境)。 当然,它的值可能会受到其他计算的影响)。 如果表达式既计算值又修改环境,则该表达式被认为具有副作用(因为它做了一些额外的事情)。 a 是一个有副作用的表达式。 这些陈述也适用于其他语言的类似问题。

现在的问题是:如果C/C++程序中的表达式(节)有副作用,那么这种副作用在使用中什么时候真正显现出来呢? 为了让问题更清楚,我们假设有一个代码片段“.a[i].A[j].”程序,假设当时I和J的值完全相等(a[i].A[j]. ] 和 a[j] 只是引用相同的数组元素); 假设 a[i] 实际上是在 a[j] 之前计算的; 假设没有其他操作修改 a[i]。 在这些假设下,a[i]对a[i]的修改是否可以反映在a[j]的评估中? 注意:由于 I 和 J 的相等问题无法静态确定,因此对这两个数组元素的访问(对内存的访问)必须由目标代码中的两个单独的代码来完成。 现代计算机中的计算是在内存中完成的。 现在的问题是:在执行获取a[j]值的代码之前,a[i]的更新值是否已经(从寄存器)保存到内存中? 如果你了解这方面的语言规律,这个问题的答案就很清楚了。

程序语言通常会指定执行中变量修改的最新实现时刻(称为序列点、顺序点或执行点)。 程序执行中有一系列连续的点。

(始终),语言保证一旦执行到达某个序列点,该点之前发生的所有修改(副作用)都必须实现(必须反映在后续对同一存储位置的访问中),而该点之后的所有修改都必须实现。不会发生。 连续点之间没有保证。 序列点的概念对于像 C/C++ 这样允许表达式有副作用的语言尤其重要。

现在上面问题的答案就很明确了:如果a[i]和a[j]之间存在序列点,那么就保证a[j]得到修改后的值; 否则不予保证。

C/C语言定义(语言参考手册)明确定义了序列点的概念。 序列点位于:

1. 在每个完整表达式的末尾。 完整的表达式包括变量初始化表达式、表达式语句、返回语句以及条件、循环、语句的控制表达式(for头中有3个控制表达式);

2. 运算符||,计算逗号运算符;

3、函数调用中所有实参和函数名表达式(也可以用表达式描述要调用的函数)求值之后(进入函数体之前)。

表达式编写求值程序是什么_表达式编写求值程序怎么写_编写程序求表达式的值

假设时间ti 和ti 1 是两个连续的点,当ti 1 到达时,任何C/C系统(VC、BC等都是C/C系统)必须知道ti之后的所有副作用。 当然,他们不一定要等到时间ti 1,他们可以选择在时间段[t, ti 1]之间的任何时间实现该期间发生的副作用,因为C/C++语言允许那些选择。

在前面的讨论中,假设 a[i] 先于 a[i] 完成。 a[i]在程序片段中是否先执行也与它所在的表达式所确定的计算过程有关。 我们都熟悉C/C语言中关于优先级、组合、括号的规则,但是有多个操作数时的计算顺序却常常被忽视。 请参阅下面的示例:

(ab)*(cd) 资金(a, b, a 5)

“*”的两个操作数哪一个先出现? fun 及其三个参数按什么顺序评估? 对于第一个表达式,使用什么计算顺序并不重要,因为其中的所有子表达式都是引用透明的。 第二个示例中的参数表达式有副作用,因此求值的顺序很重要。 少数语言明确规定了操作对象的计算顺序(Java规定从左到右),而C/C++故意没有。 它不指定大多数二元运算(除法、 || 和 , )中两个对象的求值顺序,也不指定函数参数和调整函数的求值顺序。 计算第二个表达式时,先按一定顺序计算fun、A、B、a 5,然后是序列点,然后进入函数执行。

很多书在这些问题上都是错误的(包括一些非常流行的书)。 例如,C/C 先计算左(或右),或者 C/C 系统先计算边。 这些说法都是错误的! 在同一表达式中,C/C++ 系统始终可以先计算左或右,或者有时先计算左或右,有时先计算左或右。 不同的系统可能使用不同的序列(因为它们都符合语言标准); 同一系统的不同版本可能使用不同的方法; 同一版本在不同的优化方法下可能在不同的位置使用不同的序列。 因为这些做法符合语言规范。 这里还要注意序列点的问题:即使先计算一侧的表达式,其副作用也不一定反映在内存中,因此对另一侧的计算没有影响。

回到前面的例子:“有谁知道下面的 C 语句赋予 n 的值是什么?”

m=1; n=毫米

正确答案是:我不知道! 语言并不规定要计算什么,结果完全取决于特定系统在特定上下文中的行为。 它涉及操作数的求值顺序和变量修改的执行时间。 用于:

库塔a;

我们知道是这样。

(计算。(a))。 运算符(a);

的缩写。 首先看外层函数的调用。 这里,我们需要计算所使用的函数和a的值。 该语言没有指定哪个先出现。 如果确实先计算了该函数,而在这次计算过程中又发生了另一个函数调用,并且在被调用的函数执行之前存在一个序列点,那么就会实现A的副作用。 如果先计算参数,则求a的值。

4,然后计算函数的副作用当然不会改变它(在这种情况下输出两个 4)。 当然,这些只是假设。 其实应该说的是:这种东西根本不应该写,讨论它的效果也是没有意义的。

也许有人会说,为什么人们在设计C/C++时不指定顺序,这样就可以避免这些麻烦呢? C/C语言的实践完全是有意为之,其目的就是让

允许编译器采用任意求值顺序,以便编译器在优化时可以根据需要调整实现表达式求值的指令顺序,以获得更高效的代码。

像Java一样,严格指定表达式的求值顺序和效果不仅限制了语言的实现,而且还需要更频繁的内存访问(以实现副作用),这可能会带来相当大的效率损失。 应该说,在这个问题上,C/C和Java的选择都实现了各自的设计原则,各有各的收获(C/C的潜在效率,Java更清晰的程序行为),当然丢失了。 还值得指出的是,大多数编程语言实际上都遵循类似 C/C++ 的规则。

经过这么多讨论,我们应该得出什么结论? C/C++ 语言的规则告诉我们,无法保证任何依赖于特定计算顺序以及序列中点之间修改效果的表达式的结果。 编程中应该实现的规则是:如果在任何“完整表达式”中存在对同一个“变量”的多个引用(形成以序列点结束的计算),那么不应该有任何副作用的引用这个“变量”。 否则,无法保证预期结果。 注意:这里的问题不是在某个系统中尝试,因为我们不可能测试所有可能的表达式组合和所有可能的上下文。 我们谈论的是语言,而不是实现。 总之,千万不要写这种表情符号,否则我们迟早会在某些情况下遇到麻烦。

后记:去年,我参加了一个学术会议。 我看到一位同事写了一篇文章讨论C系统中表达式求值的顺序,并总结了一些“规则”。 从讨论中我了解到一些“程序员能力测试”有这样的问题。 这让我很不安。 我今年给一个教师班讲课,发现很多专业课老师对这个基本问题都不清楚,感觉问题确实很严重。 所以我整理了这篇短文供大家参考。

以上关于C/C语言表达式求值的讨论是小编分享的全部内容。 希望能给大家一个参考,请大家多多支持。

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON