【问题标题】:Operator precedence in the given expressions给定表达式中的运算符优先级
【发布时间】:2017-10-25 21:25:05
【问题描述】:

表达式 1:*p++; 其中p 是一个指向整数的指针。

p 将首先递增,然后由于关联性(从右到左)而采用它指向的值。对吗?

表达式 2:a=*p++; 其中p 是一个指向整数的指针。

首先获取p 的值,然后首先分配给a,然后p 由于后递增而递增。对吗?

【问题讨论】:

标签: c pointers operator-precedence post-increment


【解决方案1】:

首先,让我告诉你,没有关联性也没有评估顺序实际上是相关的这里。这都是关于operator precedence。让我们先看看定义。 (强调我的

  • Precedence :在数学和计算机编程中,运算顺序(或运算符优先级)是反映关于 首先执行哪些程序来评估给定的数学表达式。

  • Associativity:在编程语言中,运算符的关联性(或固定性)是一种属性,它决定了相同的运算符如何优先级在没有括号的情况下分组

  • Order of evaluation:任何 C 运算符的操作数的求值顺序,包括函数调用表达式中函数参数的求值顺序,并且任何表达式中子表达式的求值顺序是未指定的,除了少数情况。主要有两种类型的评估:a)价值计算 b)副作用。


后自增的优先级较高,所以会先求值。

现在,值增量恰好是在“值计算”之后排序的操作的副作用。因此,值计算结果将是操作数 p 的未更改值(这里再次由于使用 * 运算符而被取消引用),然后发生增量。

引用C11,第 §6.5.2.4 章,

后缀++ 运算符的结果是操作数的值。作为一个副作用, 操作数对象的值递增(即相应类型的值 1 为 添加到它)。参见关于加法运算符和复合赋值的讨论 有关约束、类型和转换以及操作对 指针。 结果的值计算在副作用之前排序 更新操作数的存储值。 [.....]

两种情况的求值顺序相同,唯一不同的是,第一种情况下,最终的值被丢弃。

如果您“按原样”使用第一个表达式,您的编译器应该会生成有关未使用值的警告。

【讨论】:

    【解决方案2】:

    后缀运算符的优先级高于一元运算符。

    所以这个表达式

    *p++
    

    等价于表达式

    *( p++ )
    

    根据 C 标准(6.5.2.4 后缀递增和递减运算符)

    2 后缀++运算符的结果是 操作数。作为副作用,操作数对象的值是 递增(即,将适当类型的值 1 添加到 它)。查看加法运算符和复合赋值的讨论 有关约束、类型和转换以及效果的信息 对指针的操作。结果的值计算为 在更新存储值的副作用之前排序 操作数。

    所以p++ 产生指针p 的原始值作为操作的结果,并且还具有递增操作数本身的副作用。

    至于一元运算符 then(6.5.3.2 地址和间接运算符)

    4 一元 * 运算符表示间接。如果操作数指向一个 函数,结果是一个函数指示符;如果它指向一个 对象,结果是一个指定对象的左值。如果操作数 类型为“类型指针”,结果类型为“类型”。如果 无效的值已分配给指针,的行为 一元 * 运算符未定义

    所以表达式的最终结果

    *( p++ )
    

    是指针p 指向的对象的值,由于副作用,该值也会增加。这个值被赋值给语句中的变量a

    a=*p++;
    

    例如如果有以下声明

    char s[] = "Hello";
    char *p = s;
    char a;
    

    然后在这句话之后

    a = *p++;
    

    对象a 将具有字符'H',而指针p 将指向数组s 的第二个字符,即字符'e'

    【讨论】:

      【解决方案3】:

      关联性在此无关紧要。仅当您有具有相同优先级的相邻运算符时,关联性才重要。但在这种情况下,++ 的优先级高于*,因此只有优先级很重要。由于优先级,表达式等价于:

      *(p++)
      

      由于它使用后增量,p++ 会增加指针,但表达式返回指针的值之前它被增加。然后间接使用该原始指针来获取值。它实际上相当于:

      int *temp = p;
      p = p + 1;
      *temp;
      

      第二个表达式是一样的,只是它把值赋给另一个变量,所以最后一条语句变成:

      a = *temp;
      

      【讨论】:

      • 不等价。原件没有序列点,你的重写有。
      • 既然它不属于任何更大的表达式,那有什么区别呢?我只是在描述可观察到的结果。
      【解决方案4】:

      表达式

      *p++
      

      等价于

      *(p++)
      

      这是由于优先级(即:后缀自增运算符的优先级高于间接运算符

      和表达式

      a=*p++
      

      出于同样的原因,等同于

      a=*(p++)
      

      在这两种情况下,表达式 p++ 的计算结果为 p

      【讨论】:

      • 您没有回答有关事情发生顺序的问题。
      【解决方案5】:
      • v = i++;i返回到相等运算,然后赋值给v。随后,i 递增(编辑:从技术上讲,它不一定按此顺序执行)。因此v 具有i 的旧值。我记得是这样的:++ 最后写,因此最后发生。
      • v = ++i;i递增,然后返回分配给vvi 具有相同的值。
      • 当您不使用返回值时,它们的作用相同(尽管在某些情况下不同的实现可能会产生不同的性能)。例如。在 for 循环中,for(int i=0; i<n; i++)for(int i=0; i<n; ++i) 相同。后者有时会自动成为首选,因为它往往对某些对象更快。
      • * 的优先级低于++,因此*p++*(p++) 相同。因此,在这种情况下,p 被返回给 *,后者取消了对它的引用。然后p 中的地址增加一个元素。 *++p 首先增加 p 的地址,然后取消引用它。
      • v = (*p)++; 设置 v 等于 p 指向的旧值然后递增它,而 v = ++(*p); 递增 p 指向的值然后设置 v 等于它。 p中的地址不变。

      例子:如果,

      int a[] = {1,2};
      

      然后

      int v = *a++;
      

      int v = *++a;
      

      两者都会使 a 递增,但在第一种情况下,v 将为 1,在后者中为 2。

      【讨论】:

      • 错了。 "Then" 和 "Subsequently" 承诺特定的执行顺序,但是这段代码没有顺序点,不保证顺序。
      • 嗯,通常后增量运算符的实现首先递增,然后 then 返回旧值的副本。对于 POD,我想这取决于编译器(无论如何它可能会优化)。所以你是对的,当然。但是,抽象是存在的,因此您可以忽略某些工作方式的确切细节,并以更简单的方式思考它。我认为“然后”和“随后”不一定是在执行顺序方面,而是在更抽象的方面。但也许如果我是母语人士,我不会那样做,所以请随时指出这一点。
      【解决方案6】:

      *p++; 其中p 是一个指向整数的指针。

      p 将首先递增,然后由于关联性(从右到左)而采用它指向的值。对吗?

      没有。在后增量中,该值被复制到一个临时值(一个右值),然后左值作为副作用增加。

      a=*p++; 其中p 是一个指向整数的指针。

      首先取p 的值,然后首先分配给a,然后p 由于后递增而递增。对吗?

      不,这也不正确。 p 的增量可能发生在写入a 之前。重要的是存储在a 中的值是使用p 先前值的临时副本加载的。

      未指定内存读取是否发生在新值p 的内存写入之前,并且任何依赖该顺序的代码都是未定义的行为。

      这些序列中的任何一个都是允许的:

      • p 复制到临时THEN 增量p,然后在临时地址指示的地址处加载值,然后将加载的值存储到a
      • p 复制到临时THEN 加载值在临时指定的地址(此值本身被放置在临时) THEN 递增p THEN 将加载的值存储到a
      • p复制到临时THEN加载值在临时THEN存储加载值到a地址处的临时THEN加载值THEN增量p

      以下是两个未定义行为的代码示例,因为它们依赖于副作用的顺序:

      int a = 7;
      int *p = &a;
      a = (*p)++;  // undefined behavior, do not do this!!
      
      void *pv;
      pv = &pv;
      void *pv2;
      pv2 = *(pv++);  // undefined behavior, do not do this!!!
      

      括号创建序列点(或sequenced before关系,在新的措辞中)。带括号的代码版本与不带括号的代码版本一样未定义。

      【讨论】:

        猜你喜欢
        • 2011-07-24
        • 2021-10-04
        • 2016-08-20
        • 2020-06-23
        • 2013-12-28
        • 2019-02-13
        • 2013-08-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多