C++求值顺序

《C++Primer5th》中文版第124页
C++语言没有明确规定大多数二元运算符的求值顺序,
给编译器优化留下了余地。
这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,这个是否可以接受?

1.首先可以知道优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值。

比如:

int i=f1()*f2();

在这里虽然f1和f2在乘法之前被调用,但是f1先调用还是f2先调用却不得而知。

2.再比如结合律:

--

int i=0;
cout<<i<<" "<<+i<<endl;

结果可能是0 1或者是1 1.

因为虽然<<是左结合,但是对于那些没有明确规定运算对象的求值顺序的运算符而言,求值顺序就和优先级,以及结合律无关。

所以上面的式子是未定义的,即如果表达式指向并且修改了同一个对象,这样的行为就是未定义的

  • 逻辑与&&
  • 逻辑非||
  • 条件?:

    -逗号,

    上面四种运算符明确规定了运算对象的求值顺序。

3.C++手册

几乎所有 C++ 运算符的求值顺序(包括函数调用表达式中的函数参数求值顺序和任何表达式中子表达式的求值顺序)都是未指定的。编译器能以任何顺序求值,并可以在再次求值相同表达式时选择另一顺序。

例子:

表达式 f1() + f2() + f3()

由于 operator+ 的从左到右结合性分析为 (f1() + f2()) + f3() ,但运行时对 f3 的函数调用可能首先、最后,或在 f1() 和 f2() 之间求值。

4.序列点规则(以下内容来自C++手册

--

序列点规则 (C++11 前)

定义

求值可能产生副效应:即访问 volatile 左值所指代的对象、修改对象、调用库 I/O 函数或调用做任何这些动作的函数。

序列点( sequence point )是执行序列中的点,在该点所有来自序列中先前求值的副效应均已完成,而后继求值的副效应都未开始。

规则

1) 每个完整表达式结尾(典型地在分号)有一个序列点。

2) 调用函数时(无论该函数是否内联,无论是否使用函数调用语法),所有函数参数的求值(若存在)后有一个序列点,它在函数体内的任何表达式或语句执行前发生。

3) 复制函数返回值后,和函数外任何语句的执行前有一个序列点。

4) 一旦函数执行开始,则在被调用函数完成前,不求值来自调用方函数的表达式(函数不能交错)。

5) 每个使用内建(非重载)运算符的下列四种表达式的求值中,表达式 a 的求值后有一个序列点。

a && b
a || b
a ? b : c
a , b

未定义行为

  • 1) 前后序列点间,至多可以修改标量对象的存储值一次,否则行为未定义。

    i = ++i + i++; // 未定义行为
    i = i++ + 1; // 未定义行为( C++17 前)
    i = ++i + 1; // 未定义行为( C++11 前)
    ++ ++i; // 未定义行为( C++11 前)
    f(++i, ++i); // 未定义行为( C++17 前)
    f(i = -1, i = -1); // 未定义行为( C++17 前)
  • 2) 前后序列点间,访问表达式求值所修改的标量对象的先前值,必须只为确定要存储的值。若以任何其他方式访问,则行为未定义。
cout << i << i++; // 未定义行为( C++17 前)
a[i] = i++; // 未定义行为( C++17 前)

原文地址:https://www.cnblogs.com/FlyerBird/p/8996353.html

时间: 2024-09-30 06:44:23

C++求值顺序的相关文章

C之旅(一)运算符——优先级,结合性和求值顺序

本节主要讲基本运算符的优先级,结合性和求值顺序.先看一个表达式- (1 + 2) * 3 + (4 + 5 * (6 + 7 ))记住你对它的运算过程,看完下面的内容之后,也许你会用不同的方式来看待这个表达式. 基本运算符 = + - * / C中没有指数运算.运算符操作的是操作数,操作数就是放在运算符两侧的东西. (1) 赋值运算符 = year = 2016; 读作将值2016赋给year,而非year等于2016.=将2016赋给变量year,是从右到左的,即=具有右结合性.2016 =

C语言对表达式的求值顺序不是明确规定的

讨论区看到的 WA来自那些递归下降求解的代码. 第一种情况,使用|| 和 &&: 例如s为所给串 int getval() { switch(s[c_s++]) { case 'p': return (value & (1 << 0))? 1:0; case 'q': return (value & (1 << 1))? 1:0; case 'r': return (value & (1 << 2))? 1:0; case 's'

c++中函数参数的求值顺序

c++中如果函数的参数列表包含多个实参,那么对参数的求值顺序是不确定的. 在谭浩强的<C++程序设计>(第二版)P94中提到,GCC对参数求值是按从右到左的顺序求值的. 但我实测并非如此. #include <iostream> #include <iomanip> using namespace std; void test(int x, int y) { cout << 'x' << x << " y" <

Golang语句中的求值顺序

在Go specs中,有这样三点陈述: 1.变量声明(variable declaration)中的初始化表达式(initialization expressions)的求值顺序(evaluation order)由初始化依赖(initialization dependencies)决定:但对于初始化表达式内部的操作数的求值需要按照2中的顺序:从左到右: 2.在非变量初始化语句中,对表达式.赋值语句或返回语句中的操作数进行求值时,操作数中包含的函数(function)调用.方法(method)调

表达式的求值顺序

代码: #include <stdio.h> #include <stdlib.h> #include <stdbool.h> // C语言保证逻辑表达式是从左至右求值 int main(void) { // printf("Left") == 4 // printf("Right") == 5 if (!printf("Left") && printf("Right")) 

JS连续赋值与求值顺序

以下代码输出什么? 为什么? var a = {n:1}; var b = a; a = {n:2}; a.x = a ; console.log(a.x); console.log(b.x); var a = {n: 1} var b = a; a.x = a = {n: 2}; console.log(a.x); console.log(b.x) 第一个问题: a.x ---> {n:2,x:a}; b.x ---> undefined; 解答:a的值很清晰了,a第二次赋值以后变成了{n:

二元运算符求值顺序问题

对于没有指定顺序的运算符来说,如果表达式指向并修改了同一对象,将会引发错误并产生未定义行为.如: int i = 0: cout << i << " " << ++i; // 未定义 经验: 拿不准时用括号 如果改变了一个运算对象的值,在表达式的其他地方就不要在使用这个运算对象(当改变运算对象的子表达式本身就是另外一个子表达式的运算对象时,是可以的 如:*++iter

关于C/C++ 表达式求值顺序 未定义

http://blog.csdn.net/zhongjiekangping/article/details/5164132 这篇文章讲的真好! 对于 "expr1 + expr2",(其中expr1,expr2都可能是复合表达式),不同的编译器想先算exp1就先算exp1,想先算exp2就先算exp2.而且,编译器不必保证exp1计算完毕之后才去计算expr2,它可以这么做,也可以不这么做.C语言标准里并不做规定. 一段正常的程序不应该由于加号前后的求解顺序不同而得到不同的结果.因此,

连续赋值与求值顺序var a = {n:1};a.x = a = {n:2}; alert(a.x);

代码如下: <script> var a = {n:1}; var b = a; a.x = a = {n:2}; console.log(a.x);// --> undefined console.log(b.x);// --> [object Object] </script> 上面的例子看似简单,但结果并不好了解,很容易把人们给想绕了——“a.x不是指向对象a了么?为啥log(a.x)是undefined?”.“b.x不是应该跟a.x是一样的么?为啥log出来居然