这个问题已经有了答案:
Why are these constructs using pre and post-increment undefined behavior?
14个答案
我还有一个C指针问题。
考虑执行以下程序:
int x[5] = {0,3,5,7,9};
int* y = &x[2];
*(y+2) = *(y--);
之后数组
x
保持什么值?到底是怎么回事?我知道
y--
是如何工作的,并且理解其余部分,但不知道*(y+2)
如何与其余部分联系在一起。另外,给出的答案是
y--
。 最佳答案
在这种情况下,你不应该相信你教授给出的答案。
继续温特穆特的回答…
问题在于声明
*(y+2) = *(y--);
表达式
y--
的计算结果为当前值y
,并作为副作用使变量递减。例如:int a = 10;
int b;
b = a--;
在对上述表达式求值之后,
b
将具有值10,a
将具有值9。但是,C语言不要求在表达式求值之后立即应用副作用,只要求在下一个序列点之前应用副作用(在本例中是在语句的末尾)。也不要求从左到右计算表达式(有几个例外)。因此,不能保证
y
中的y+2
值代表减量操作前后的y
值。c语言标准显式地将类似这样的操作调用为未定义的行为,这意味着编译器可以自由地以任何方式处理这种情况。结果将根据编译器、编译器设置甚至周围的代码而有所不同,任何答案在语言定义方面都同样正确。
为了使这个定义明确并给出相同的结果,您需要在赋值语句之前递减
y
:y--;
*(y+2) = *y;
这一直是C语言中最容易被误解和误教的方面之一。如果你的教授期望这个结果是明确的,那么他对语言的了解就不如他认为的那么好。再说一次,他在这方面并不独特。
在Wintermute发布的C 2011 draft standard片段上重复和展开:
6.5表达式
…
2如果标量对象上的副作用相对于另一个副作用未排序
关于同一标量对象或使用同一标量的值计算
对象,行为未定义。如果有多个允许的
表达式的子表达式,如果这样一个未排序的边
任何命令都会产生效果。84)
3运算符和操作数的分组由语法指示。85)除非指定
后来,子表达式的副作用和值计算被取消排序。
84)本段呈现未定义的语句表达式,如
i = ++i + 1;
a[i++] = i;
同时允许
i = i + 1;
a[i] = i;
85)语法指定表达式求值中运算符的优先级,这是相同的
作为本子条款的主要子条款的顺序,最高优先级优先。因此,例如
允许作为二进制
+
运算符(6.5.6)的操作数的表达式是在6.5.1至6.5.6。例外情况是将转换表达式(6.5.4)作为一元运算符的操作数
(6.5.3),以及包含在下列任一对运算符之间的操作数:分组
括号
()
(6.5.1)、下标括号[]
(6.5.2.1)、函数调用括号()
(6.5.2.2)和条件运算符
? :
(6.5.15)。在每个主要子类中,运算符具有相同的优先级。左或右关联性是
在每个子条款中由其中讨论的表达式的语法指示。
86)在程序执行过程中多次求值的表达式中,未排序
不需要在
不同的评价。
增加了重点。请注意,这是自C89标准以来的事实,尽管此后措辞有所改变。
“未排序”只是指不能保证一个操作在另一个操作之前完成。赋值运算符不引入序列点,因此不能保证表达式的lhs在rhs之前求值。
现在说重点-你的教授显然希望这类表达有一个特定的行为。如果他做了一个测试或者一个小测验,询问像
a[i] = i--;
这样的结果会是什么,他可能不会接受“行为是未定义的”的答案,至少不会自己接受。你可能想讨论温特穆特和我给他的答案,以及上面引用的标准章节。