我正在学习C,我的老师给了我们一些功课。我们必须确定一些代码的输出。我不明白y=4
的方式。
代码如下
int main() {
int w = 3, y = 3;
printf("w = %i\n", --w + w--);
printf("y = %i\n\n", y = w + y++);
return 0;
}
答案 0 :(得分:5)
由于您在两个序列点之间修改变量值两次,因此行为未定义。
您应该阅读有关序列点http://en.wikipedia.org/wiki/Sequence_point的更多信息。这就是标准所说的:
在称为序列点的执行序列中的某些特定点处,先前评估的所有副作用应完整,并且不会发生后续评估的副作用。
答案 1 :(得分:4)
让我以重要的公共服务公告为前言:
当你的编译器给你一个警告,任何警告时,你应该注意它。您的问题中的代码示例会产生未定义的行为 - 换句话说,C标准并不保证每个编译器在给定代码时都会给出相同的结果。这总是一件坏事。
除此之外,这里是对正在发生的事情的解释。
我稍微修改了你的代码以查看w:
的中间值#include <stdio.h>
int main(void) {
int w = 3, y = 3;
printf("w = %i\n", --w + w--);
printf("w is actually %d\n", w);
printf("y = %i\n\n", y = w + y++);
return 0;
}
我的编译器抱怨此代码时出现以下警告:
order.c:6:24: warning: multiple unsequenced modifications to 'w' [-Wunsequenced]
printf("w = %i\n", --w + w--);
^ ~~
order.c:8:35: warning: multiple unsequenced modifications to 'y' [-Wunsequenced]
printf("y = %i\n\n", y = w + y++);
~ ^
2 warnings generated.
输出
w = 4
w is actually 1
y = 4
发生了什么事?由于编译器从左到右解析语句,语句
--w + w--
似乎导致以下步骤(对于我的编译器):
--w : decrement w, it is now 2
w-- : use the value of '2', but decrement w when you are done
结果是总和为2+2
,第一行打印4
,但在求和之后,w
递减为1
。但是你不能依赖这种行为 - 这就是编译器发出警告的原因。正如Eric Postpischil所指出的,可能存在执行顺序可能不同的情况。 “不要这样做”是底线 - 你正在追求未定义的行为。
请注意,您的程序打印w equals 4
这一事实并不意味着任何时候都是如此。如您所见,当您实际打印出w
时,它是1。
假设编译器按上述顺序执行了这些语句,我们转到下一行:
y = w + y++
我们从w = 1
和y = 3
开始。这两者的总和是4
,这是表达式的值
y = w + y++
因此是印刷品(4)。
作为副作用,y递增。碰巧的是,无论哪种方式,y的值最后都是4。如果您更改代码以便从w = 5
开始,则最终会得到y = 6
。换句话说,y++
赋值覆盖了y =
的效果。
答案 2 :(得分:3)
此代码中至少有这两部分存在未定义的行为(如Why are these constructs (using ++) undefined behavior?中所述):
--w + w--
y = w + y++
这意味着您获得的结果不可移植或无法保证。
除了未定义的行为之外,输出实际上就在于你。 printf("w = %i\n", --w + w--);
看起来正在打印w
的值(因为它打印w = …
),但它不会打印w
的值,而是打印{{1}的值(这是未定义的,所以它实际上打印了编译器编译--w + w--
的任何值)。
--w + w--
的价值是什么?您的编译器为w
生成的代码具有副作用,使--w + w--
的值为w
,因为它原来是{{1}并且1
和3
都会减少--w
。但是,w--
的值(在您的编译器生成的代码中,基于您获得的输出,w
。可能导致此问题的原因是什么?可以先评估--w + w--
,将4
从--w
递减到w
,然后返回{em>新值3
,是2
。然后可以评估w
,将2
从w--
递减到w
,但会返回2
的原始值,是1
。w
和2
的总和当然是2
。
因此,2
的值为4
,--w + w--
的新值为4
。
w
会发生什么? 1
的新值为y
,因此当您执行时(回想w
的值为1
):
y
3
的副作用是printf("y = %i\n\n", y = w + y++);
递增(但是在分配之前或之后是否发生这种情况尚未定义),但表达式的值是之前的y++
值,在本例中为y
。因此,您要添加y
,其值为3
,以及w
的值,即1
。然后,您将 y++
分配给3
,并且该分配的值为4
,因此您将获得y
作为输出。 4
中仍然存在可能未定义的行为,因为当 4
的增量应该发生时,不明确:在分配之前或之后?
答案 3 :(得分:1)
您的老师正在承担特定的评估顺序,但该语言无法保证。
假设严格的从左到右的评估,和假设立即应用副作用,那么给出w
和y
的起始值,会发生以下情况:
--w
评估为w-1
(2),副作用递减w
; w
现在是2 w--
评估为w
(2),副作用递减w
; w
现在是1 --w + w--
评估为4 y++
评估为y
,副作用增加y
; y
现在是4 w + y++
评估为1 + 3
或4 y = w + y++
将{4}分配给y
。的 BUT ... 强> 的
正如我上面所说,这种行为不受语言保证;除少数特定情况外,表达式中的评估顺序为未指定,因此y
为4只是许多可能结果中的一个,所有就语言而言,它们被认为是同等正确的。
任何表达式的行为,它采用以下形式(++
和--
,包括前缀和后缀):
x++ + x++
a[i++] = i
y = y++
未定义。同样,法案的行为如
foo(y++, y++);
也未定义。