有人能解释一下这段代码中的优先级是怎么回事吗?我一直想弄清楚自己发生了什么,但我无法独自处理。
#include <stdio.h>
int main(void) {
int v[]={20,35,76,80};
int *a;
a=&v[1];
--(*++a);
printf("%d,%d,%d,%d\n",v[0],v[1],v[2],v[3]);
(*++a);
printf("%d\n", *a);
*a--=*a+1; // WHAT IS HAPPENING HERE?
printf("%d\n", *a);
printf("%d,%d,%d,%d\n",v[0],v[1],v[2],v[3]);
}
//OUTPUT
20,35,75,80
80
75
20,35,75,76
答案 0 :(得分:2)
*a--=*a+1; // WHAT IS HAPPENING HERE?
正在发生的行为是 undefined 。
6.5表达式
...
2如果相对于不同的副作用,对标量对象的副作用未被排序 在相同的标量对象上或使用相同标量的值进行值计算 对象,行为未定义。如果有多个允许的排序 表达式的子表达式,如果这样一个未经测序的一方,则行为是不确定的 任何顺序都会产生影响。 84)
3运算符和操作数的分组由语法指示。 85)除非另有说明 后来,子表达的副作用和价值计算都没有排序。 86)
表达式*a--
和*a
相对于彼此未经过排序。除少数情况外,C不保证表达式从左到右进行评估;因此,不能保证在*a--
之前评估*a
(并应用副作用)。
*a--
有副作用 - 它会更新a
以指向序列中的上一个元素。 *a + 1
是一个值计算 - 它将a
当前指向的值加1。
根据*a--
和*a
的评估顺序以及实际应用--
运算符的副作用,您可以指定v[1] + 1
的结果到v[0]
,或v[1] + 1
到v[1]
,或v[0] + 1
到v[0]
,或v[0] + 1
到v[1]
,或别的东西。
由于行为是 undefined ,编译器不需要特别做任何事情 - 可能发出诊断并停止翻译,可能> em>发出诊断并完成翻译,或者可以完成翻译而无需诊断。在运行时,代码可能崩溃,可能获得意外结果,或者代码可能按预期工作。
答案 1 :(得分:1)
我不打算解释整个节目;我将专注于&#34;发生在这里的事情&#34;线。我想我们可以同意,在此行之前,v[]
数组看起来像这样,a
指向v
的最后一个元素:
+----+----+----+----+
v: | 20 | 35 | 75 | 80 |
+----+----+----+----+
0 1 2 3
^
+-|-+
a: | * |
+---+
现在,我们有
*a-- = *a+1;
看起来这将分配到a
点的位置,并递减a
。所以看起来它会为v[3]
指定一些内容,但让a
指向v[2]
。
分配的值显然是a
指向的值加上1.
但关键问题是,当我们在右侧采用*a+1
时,它会在减少之前或之后使用a
的旧值或新值。右侧?事实证明这是一个非常非常难以回答的问题。
如果我们在减量后取值,它将被a[2]
加上1或76分配给a[3]
。它看起来就像你的编译器如何解释它。这有一定的意义,因为当我们从左到右阅读时,很容易想象当我们开始计算*a+1
时,a--
已经发生了
或者,如果我们在减量之前取值,那么a[3]
加1或81将被分配给a[3]
。这就是我试过的三种不同编译器的解释方式。这也有一定的意义,因为当然分配实际上是从右到左进行的,所以很容易想象*a+1
在 {{1}之前发生在左侧。
那么哪个编译器是正确的,你的还是我的,哪个错了?这是答案有点奇怪和/或令人惊讶的地方。答案是既没有编译器错误。这是因为事实证明,这不仅仅是很难确定应该在这里发生什么,而是(根据定义)不可能来弄清楚这里发生了什么。 C标准没有定义该表达式的行为方式。事实上,它比定义表达式应该如何表现更远:C标准明确表示该表达式是 undefined 。因此,你的编译器在a--
中放置76是正确的,而我的编译器是正确的,因为&#34;未定义的行为&#34;意味着任何可能发生,编译器安排将其他数字放入v[3]
或最终分配给{{1}之外的其他内容并不是错误的。 }}
所以答案的另一部分是你不能写这样的代码。您不能依赖于未定义的行为。它会在不同的编译器下做不同的事情。它可能会做一些完全不可预测的事情。理解,维护或解释是不可能的。
由于评估顺序模糊,表达未定义时很容易检测到。有两种情况:(1)同一变量被修改两次,如v[3]
。 (2)同一个变量在一个地方被修改,并在另一个地方使用,如v[3]
。
值得注意的是,我使用过的三个编译器之一&#34; eo.c:15:警告:无序修改和访问&#39;&#39;&#34;和另一个说&#34; eo.c:15:5:警告:'a'上的操作可能未定义&#34;。如果您的编译器有选项可以启用这些警告,请使用它! (在gcc下x++ + x++
或*a-- = *a+1
。在clang下,它是-Wsequence-point
或-Wall
。)
请参阅John Bode的答案,了解C标准中的详细语言,该表达式未定义。另请参阅有关此主题的规范StackOverflow问题Why are these constructs (using ++) undefined behavior?
答案 2 :(得分:-1)
不完全确定您遇到问题的表达方式。增量和减量运算符具有最高优先级。取消引用之后。加,减,后。
但是关于作业,C没有指定评价顺序(从右到左或从左到右)。 will right hand side of an expression always evaluated first
C没有指定首先评估=运算符的右侧或左侧。
*a--=*a+1;
所以可能是你的指针a首先递减或者在右侧取消引用之后。
换句话说,根据编译器,这个表达式可以等同于:
a--;
*a = *a+1;
或
*(a-1)=*a+1;
a--;
我个人从不过分依赖我的代码中的运算符优先级。我将括号或分开放在不同的行中使其更清晰。
除非您自己构建编译器并且需要决定要生成哪个汇编代码。