`
20386053
  • 浏览: 461648 次
文章分类
社区版块
存档分类
最新评论

C/C++中优先级、结合方向与执行顺序的关系

 
阅读更多
原文在:http://blog.sina.com.cn/s/blog_93b45b0f0100x47j.html

  知道语言是有以分号结束的语句组成,但语句并不是程序处理的最小单位。理论上讲程序应该以每个函数(方法)为一个语句,而操作符有可以理解成函数。但是那样做的话,程序会很难看,所以通常的语言以表达式为程序的最小执行单位。一个语句本质上说是一个表达式,但是也可以说成是逗号符,分开的若干个表达式,只是有逗号符连接起来的最终也是一个表达式。
  估计表达式的值是判断程序运行对错的核心内容,语句与语句之间的顺序基本右分号;隔开呈现“顺序”的状态,但是语句内部的执行过程却是非常复杂的。
  首先要高清楚三个概念:优先度,结合方向,执行顺序
  三个中最容易让我们忽视的就是执行顺序,通常我们认为结合方向就是执行顺序。
  其实优先级的概念我们都懂,最简单的例子就是+号和*号。我们应该先执行乘号。在每种编程语言中,各种操作符号都有自己的优先度(确定的),他们不会随着着自己的用途(运算符重载)发生变化。因为编译器需要通过优先都来确定混合表达式(多个操作符,多个操作数交杂在一起)中如何关联操作数与操作符。
1+2*3。
  上面的例子就是这样,系统可以根据优先度判断*号的操作符为2和3,而+号的操作符为1和(2*3)。只有这样确定的过程才有一个合理的结果。
所以优先度的定义并不是,哪个操作符更先执行,而是那个操作符可以更先于旁边的操作数结合,虽然结合就是为了操作,但是两者的区别还是很大的。把操作符看做是函数(实际上就是),然后根据函数中函数参数的结合问题来分析表达式的运行结果。
  那么结合方向又是什么呢?这个也是我们容易认为成执行顺序的东西,其实结合方向就是当两个操作符具有同等优先度的时候,谁更有优先度呢!这就是解决类似于:
1+2+3;
i=j=2;
的问题。1+2+3,可能不太重要,但是i=j=2。就太重要了。
  所以优先度和结合方向只是规定符合在一起的操作符与操作数之间如何结合,也就是说那个操作符操作那些操作数,这些必须在编译过程就确定,要不然很多表达式的结果的类型都有可能不确定,这与编译强类型语言的特点相违背。实际上,编译器也是遵守优先级和结合方向来对程序的表达式进行解析的。
  但是!注意,编译器并不执行程序,只是分析程序的结构。从而可以得到某个表达式最终的类型,通过这些类型可以判断程序是否符合某些规则,而并不知道表达式的值。
所以,优先度和结合方向只是为了给将来执行的时候做一个准备,(将函数名和参数理清)而不是要执行。
  所以优先度和结合方向没有那个先执行的暗示。但是由于优先度和结合方向往往决定了一个操作符的结果是另外一个操作符的操作数,那么由于操作数的得到一定先于操作符,所以就间接的安排了很多的执行顺序。
  但是,这并不一定能够安排全部的执行顺序。
i++!=j++;
  上面,根据结合度和优先度,可以看成(i++)!=(j++)。但是你不能说i++会比j++执行的早。或许有些机器会尊从从左到右的执行顺序,但是这不一定。(只是具体的编译器实现,语言层面上并没有做任何规定)。
  或许上面的例子不足以说明执行顺序的不同能改变什么东西。
int a[2];
*a++!=*a;
  根据优先度和结合方向,可以看成(*(a++)!=(*a)。由于你不能确定(*(a++)和(*a)谁先执行,你就不能叛定右边的(*a)中的a的值是多少。这里即使能编译成功,也不一定会按照我们思考的方式运行(虽然通常是按照的)。于是根据这个表达式内部执行顺序的不确定性的特定,我们建议尽量不要再同一个句子中改变某个值,然后又在该表达式中使用该值,除非,能够确定它们的执行顺序(有些方法的)。
  a=++i;就是这样一个例子,它改变i的值,并使用了它,但是由于++i,是a=的操作数,所以他们的执行顺序是确定的。有些数上说不要高于一次的使用,我认为这并不确切,关键是如果你确定它们的执行顺序就可以。
  这既是执行顺序的问题。
  句子之间的顺序有句子来管理。句子内部的顺序则理论上是没有管理的,是乱的。
  优先级和结合方向的目的并不是为了规定执行的顺序,它们的目的是为了确定每个操作符的操作数据(虽然这样一定程度上决定了执行顺序)。
  那么C/C++语言真的没有限制表达式内部执行顺序的东西吗?有的,但是不能覆盖全部。
  (1),逗号运算符,规定必须从左到右执行。
  (2)&&,||这样的逻辑运算符,也是必须从左到右执行,必须先判断左边的。
  (3)?:三目运算符,也是这样的。必须先判断问号前面的,然后向右看。
  逗号运算符这样做,我们就不用管了,主要的原因是如果不怎么做很容易搞混淆。
  逻辑运算符和三目条件运算符这样,使为了让跟好的提高效率。前面我们也作了这方面的讨论。不需要执行的(执行了也没有用),就不要管咯。
  当然也有人认为,这样做会高混淆,其实这就是语言设计者在效率和混淆之间取了一个权衡。
  除了上面上种运算符以外,其他的都不规定执行顺序。

————————————————
以下是2012.6.25新加入的内容
————————————————
  我们现在还差一个问题要解决:
  为什么C/C++要这样呢?为什么不想java中那样,规定表达式中的执行顺序为左到右呢?
  问题变得比较复杂,我们从语言当初设计的角度来看看。
  首先,操作符可以看做是函数,那么复合表达式,就是函数嵌套函数(函数的参数来自函数的另外一个结果)。前面我们提到,优先级和结合方向,让这种函数与函数参数的归宿确定下来。但是我们仍然无法确定,函数的几个参数的先后顺序。
(i++)+(i++);
  好吧,我们最终确定,表达式的执行顺序问题,就是所谓的函数的各个参数的执行先后问题。这个问题在C/C++语言中是不规定的,而在java中是规定从做到右的。
  这样的区别在于这两种语言所要设计达到的目标是不同的,C/C++这样,可以让编译器对一个表达式的执行过程有着较高的自由度,但是这会出现很多的程序员所不能预见的结果;java那样则是为了让程序更为直观,唯一,但是这样也就是放弃了编译器这方面的性能优化的机会。
  那么你会说程序的最高要求是准确,似乎C/C++这样“疯狂”的追求高性能有时候并不能保证准确性。这是有一定道理的。但是,像我们上文讨论过的,只要表达式中,别改变值的变量不被二次应用,那么这个问题就不是问题。
  是这样的,我们在编程序的过程中,一句话(可能是复杂表达式)的最直观的目的应该就是要改变某个值,所以在大部分的程序中,我们只有一个变量被改变,并且这个变量被改变之后是不会再被当前语句所引用的。基于这个常态,那基本上一个表达式中的先后执行顺序基本是不会导致最终结果的不同的。而不定死先后顺序,编译器就可以充分的在上面做文章,这样就可以做到很好的性能优化(比如可以减少都内存的次数)。
  所以,编程的时候,最后要此那种“一句话干一件事”的风格,不要追求一个“复杂的丰富的表达式”。
  一个复杂的表达中,有可能某个变量被改变了(i++,i--),而这个i还在别的地方被引用了(不管是读还是写),这里就有两个情况值得我们去思考。
  (1)即使根据优先级和结合方向,你可以确定这个变量被改变和被应用的先后顺序。程序仍然可能不会按照你想的方向发展。什么意思呢? 我的意思是说:
  即使你能够确定i++,和a[i]先后顺序,你也无法肯定,a[i]中的i是原始值,还是被自增后的值。为什么呢?因为C/C++语言不保证,i++这个语句的“副作用”能够马上被其他地方看见,(可能新的结果还是存在寄存器中),而a[i]则从内存中去取久的值。对于这一定不同的实现,有不同的规定,而C/C++语言上,只规定在某个规定的时间时,必须要做到“副作用”被看见。(通常就是语句结束)。同样这样的结果也一样是为了提供编译器足够的灵活空间去优化代码。
  (2)如果不能通过优先级和结合方向确定,变量被改变和被应用的先后顺序,那么表达式的结果更是一个undefined问题。或许,许多的编译器实现已经给出了一个比较通用的“模式”,但是,作为C/C++设计的最初想法,就是不应该有固定的“模式的”。

  所以仍然还是那句话,在一个句子里面,只做一件事。复杂的表达式必须保证被修改的变量不会再被当前语句应用。否则会出现undefined结果,编程的时候应该尽量避免这种编程风格。




分享到:
评论

相关推荐

    C_C++运算符优先级列表.pdf

    下面将根据提供的资料详细解释C/C++中的运算符、它们的优先级以及结合方向。 #### 一、最高优先级运算符 **1. 数组下标 [ ]** - **名称或含义**: 数组下标 - **实用形式**: 数组名[常量表达式] - **结合方向**: ...

    C/C++中运算符的优先级、运算符的结合性详解

    在C/C++编程语言中,理解和掌握运算符的优先级和结合性是非常关键的,因为它们决定了表达式的计算方式。本文将深入解析这两个概念,并通过实例来解释它们的影响。 首先,我们来谈谈运算符的优先级。运算符的优先级...

    C语言优先级.docx

    C语言的运算符优先级是编程时必须了解的重要概念,它决定了表达式中不同部分的计算顺序。优先级从高到低分为多个级别,每个级别的运算符具有不同的结合性,即运算的方向。以下是对C语言运算符优先级的详细解析: ...

    运算符优先级.docx

    - 同优先级的运算符,其结合方向决定了计算顺序。大多数运算符是从左到右结合的,这意味着表达式从左向右计算,例如`a + b + c`会先计算`a + b`,然后将结果与`c`相加。 - 但有三个运算符是从右到左结合的:单目...

    2023年四川计算机C语言考试笔试真题含答案.doc

    17. **C/C++中的运算符优先级**:`--y`优先执行,然后是`x++`,最后是`y++`。根据运算符的优先级和结合性,这段代码会先将y减1,然后将x加1,最后y再加1。因此,a、b、c的值分别为7、9、7。 18. **逻辑运算符**:`|...

    在DEV C 环境下C语言自加自减运算符使用分析.pdf

    在C语言中,自加(++)和自减(--)运算符是非常重要的操作,尤其在使用Dev C++环境进行程序开发时,理解这些运算符的使用方法和它们在复杂表达式中的求值情况至关重要。自加和自减运算符属于C语言的单目运算符,...

    华为2019网络精英挑战赛初赛模拟题-基础开发c++方向.docx

    ### 华为2019网络精英挑战赛初赛模拟题-基础开发C++方向知识点解析 #### C++基础知识及编程规范 1. **华为公司对待网络安全的态度**: - **知识点**:华为公司高度重视网络和业务的安全性保障,将其视为与公司的...

    《面向对象程序设计C++》期末试卷及标准答案(好的).pdf

    - 不同运算符在表达式中的执行顺序和方向(左到右或右到左)。 文档中还提到了一些特定的问题和答案,例如在选项A、B、C和D中选择正确的答案,这可能涉及上述知识点的具体应用。由于文档是通过OCR扫描得到的,因此...

    谭浩强 C++

    - **运算符的优先级与结合性**:了解不同运算符之间的优先级顺序及结合方向。 - **类型转换**:自动类型转换与强制类型转换的区别。 #### 三、面向过程的程序设计篇 ##### 第3章:程序设计初步 - **算法的概念**:...

    高质量C++_C编程指南

    - 了解不同运算符的结合方向,如左结合还是右结合。 **4.2 复合表达式** - 使用括号明确表达式的执行顺序,避免歧义。 **4.3 IF语句** - 使用清晰的条件判断,确保逻辑正确。 - 避免复杂的条件表达式,提高可读性...

    C++程序设计

    表达式则是由操作符和操作数组成的,用于计算值的代码片段,掌握不同操作符的优先级和结合方向,对于编写正确的算术、逻辑和位运算表达式至关重要。 ### 三、面向过程的程序设计 #### 第3章 程序设计初步 面向...

    C语言附录 谭浩强老师

    在谭浩强教授的教材中,附录C详细列出了C语言的所有运算符、它们的含义以及结合方向,如: - 圆括号`()`用于改变表达式的计算优先级,使其内部的运算先于外部的运算进行。 - 下标运算符`[]`用于访问数组元素。 - ...

    C语言:从放弃到入门.pdf

    流程控制决定了程序的执行顺序。 - **流程图**:用图形化的方式表示程序的执行流程。 - **常用制图工具**:如Microsoft Visio、Dia等。 ##### 四、常/变量与数据类型 **C语言中的关键字**:如`int`、`float`、`if`...

    Java语言程序设计控制语句PPT学习教案.pptx

    7. **操作数执行顺序、操作符优先级和结合方向**:理解这些规则对于编写准确的代码至关重要。不同的运算符有不同的优先级,例如乘法和除法高于加法和减法,括号内的表达式先于外部的运算。结合方向是指具有相同...

    The C Cheat Sheet - An Introduction to Programming in C

    - 结合性决定了当两个相同优先级的运算符出现在同一表达式中时,它们的结合方向。 #### 5.0 程序结构 - **5.1 函数声明** - 为了能够调用函数,需要先对其进行声明。 - 函数声明通常包括返回类型、函数名和参数...

    C语言编程要点

    这意味着在没有括号的情况下,多个相同优先级的运算符会按照其结合性的方向进行组合。例如: ```c int x = 10 + 20 - 5; // 自左至右:(10 + 20) - 5 int y = 5 - 20 + 10; // 自左至右:(5 - 20) + 10 ``` #### ...

    5.4测试.docx

    - **题目示例**:选择题第2题考察了C++相较于C语言的主要改进方向。正确答案为B. 面向对象。 **3. 封装的概念及实现方式** - **知识点解释**:封装是面向对象编程的一个重要特征,它指的是将数据和与这些数据相关...

    Java语言程序设计控制语句PPT课件.pptx

    8. **操作数执行顺序、操作符优先级和结合方向**:理解这些规则有助于正确编写和理解复杂的表达式。通常,括号具有最高优先级,然后是乘法和除法,接着是加法和减法。在同一优先级的运算符之间,遵循从左到右的结合...

Global site tag (gtag.js) - Google Analytics