微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

运算符结合性,优先级

如何解决运算符结合性,优先级

我只是想知道,对于以下代码,编译器是否单独使用关联性/优先级或其他一些逻辑来评估。

int i = 0,k = 0;

i = k++;

如果我们根据结合性和优先级进行评估,则 postfix ++ 的优先级高于 =,因此首先评估 k++(变成 1)然后是 {{ 1}},现在 k 的值是 = 分配给 1

因此 ii 的值将是 k。但是,1i is 0 的值。

所以我认为编译器将这个 k is 1 拆分为两个 i = k++;。因此,这里的编译器不会针对语句关联性/优先级,它也会拆分行。有人能解释一下编译器如何解析这些类型的语句吗?

解决方法

++ 做了两件事。

k++ 做了两件事:

  • 在执行任何增量之前,它的值为 k
  • 它递增 k

这些是分开的:

  • 产生 k 的值是 i = k++; 主要评估的一部分。
  • 递增 k 是一种副作用。它不是主要评估的一部分。程序可能会在计算表达式的其余部分后或在计算过程中增加 k 的值。它甚至可以在表达式的其余部分之前递增值,只要它“记住”用于表达式的预递增值。

不涉及优先级和结合性。

这实际上与优先级或关联性无关。 ++ 运算符的增量部分始终与表达式的主要计算分开。无论是否存在其他运算符,用于 k++ 的值始终是增量前的 k 值。

补充

重要的是要理解 ++ 的增量部分与主计算分离,并且在时间上有点“浮动”——它没有锚定到代码中的某个位置,而你确实做到了发生时无法控制。这很重要,因为如果操作数有其他用途或修改,例如在 k * k++ 中,增量可能发生在其他事件的主要评估之前、期间或之后。发生这种情况时,C 标准不会定义程序的行为。

,

后缀运算符的优先级高于赋值运算符。

带有赋值运算符的表达式

i = k++

包含两个操作数。

它等价地可以重写为

i = ( k++ );

表达式 k++ 的值为 0。因此变量 i 将获得值 0

赋值运算符的操作数可以按任意顺序计算。

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

2 后缀++运算符的结果是操作数的值。 作为副作用,操作数对象的值会增加(即 即,将相应类型的值 1 添加到其中)。

And(6.5.16 赋值运算符)

3 赋值运算符将值存储在由指定的对象中 左操作数。赋值表达式具有左边的值 赋值后的操作数,111) 但不是左值。的类型 赋值表达式是左操作数之后的类型 左值转换。 更新存储值的副作用 左边的操作数在左边的值计算之后排序 和右操作数。操作数的计算是无序的。

,

它类似于(只是为了说明而增加了一个序列点):

i = k; // i = 0
k = k + 1;  // k = 1
,

与 C++ 不同,C 没有“通过引用传递”。仅“按值传递”。我要借一些C++来解释。让我们将后缀和前缀的 ++ 功能实现为常规函数:

// Same as ++x
int inc_prefix(int &x) { // & is for pass by reference
    x += 1;
    return x;
}

// Same as x++
int inc_postfix(int &x) {
    int tmp = x;
    x += 1;
    return tmp;
}

所以你的代码现在相当于:

i = inc_postfix(k);

编辑:

对于更复杂的事情,它并不完全等效。例如,函数调用引入了序列点。但以上足以解释 OP 会发生什么。

,

运算符关联性不适用于此处。运算符优先级仅说明哪个操作数坚持哪个运算符。在这种情况下它不是特别相关,它只是说表达式应该被解析为 i = (k++); 而不是 (i = k)++; ,这没有任何意义。

从那时起,这个表达式的计算/执行方式由每个运算符的特定规则指定。后缀运算符被指定为 (6.5.2.4):

结果的值计算在副作用之前排序 更新操作数的存储值。

也就是说,k++ 肯定会评估为 0,然后在稍后的某个时刻,k 增加 1。我们真的不知道什么时候,只知道它发生在计算 k++ 的点之间,但在下一个序列点之前,在本例中为行尾的 ;

赋值运算符表现为 (6.5.16):

更新左操作数的存储值的副作用是 在左右操作数的值计算之后排序。

在这种情况下,= 的右操作数在更新左操作数之前计算其值。

实际上,这意味着可执行文件可以是这样的:

  • k 的计算结果为 0
  • i 设置为 0
  • k 增加 1
  • 分号/序列点

或者这个:

  • k 的计算结果为 0
  • k 增加 1
  • i 设置为 0
  • 分号/序列点
,

这里的基本问题是优先不是思考什么的正确方式

i = k=+;

意思。

让我们谈谈 k++ 的实际含义。 k++ 的定义是如果给你 k 的旧值,然后在 k 的存储值上加 1。 (或者,换种说法,它取 k 的旧值加 1,并将其存储回 k,同时为您提供 k 的旧值。)

就表达式的其余部分而言,重要的是 k++ 的值是什么。所以当你说

i = k++;

i 中存储了什么?”问题的答案是“k 的旧值”。

当我们回答“i 中存储了什么?”这个问题时,我们根本不考虑优先级。我们考虑后缀 ++ 运算符的含义。

另见this older question

后记:另一件你必须非常小心的事情是当你考虑附带问题时,“它什么时候将新值存储到 k 中?事实证明这是一个很难回答的问题,因为答案并不像您希望的那样明确。新值会在它所在的较大表达式结束之前的某个时间存储回 k(正式地,“在下一个序列点之前”),但我们不不知道它是发生在事物被存储到 i 的点之前还是之后,还是在表达式中其他有趣的点之前或之后。

,

优先级和结合性仅影响运算符和操作数彼此关联的方式 - 它们不影响表达式的计算顺序。优先规则规定

i = k++

被解析为

i = (k++)

而不是像

(i = k)++

后缀 ++ 运算符具有 结果副作用。在表达式中

i = k++
k++

resultk 的当前值,它被分配给 i副作用是增加k

逻辑上等价于写作

tmp = k
i = tmp
k = k + 1

需要注意的是,对 i 的赋值和对 k 的更新可以以任何顺序发生 - 这些操作甚至可以相互交错。重要的是 i 在递增之前获得 k 的值,而 k 获得递增,不一定是这些操作发生的顺序。

,

啊,这是一个很有趣的问题。为了帮助您更好地理解,这就是实际发生的情况。

我将尝试使用 C++ 中的一些运算符重载概念来解释,所以如果您不了解 C++,请耐心等待。

这是重载 postfix-increment 运算符的方式:

int operator++(int) // Note that the 'int' parameter is just a C++ way of saying that this is the postfix and not prefix operator
{
    int copy = *this;  // *this just means the current object which is calling the function
    *this += 1;
    return copy;
}

本质上,postfix-increment 运算符的作用是创建操作数的副本,增加原始变量,然后返回副本

在您的 i = k++ 情况下,k++ 实际上首先发生,但返回的值实际上是 k(将其视为函数调用)。然后将其分配给 i

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。