在C语言中调用函数时的序列点以及未定义/未指定的行为。

4

我正在努力理解C语言中的序列点,只是想确认一些事情。目前,我相信(1)是未定义的,而(2)仅仅是未指定的,因为在(2)中,在评估gh的参数后有序列点(所以我们不会在序列点之间两次修改i),但是f的参数的评估顺序仍然是未指定的。我的理解正确吗?

#include <stdio.h>

int g(int i) {
    return i;
}

int h(int i) {
    return i;
}

void f(int x, int y) {
    printf("%i", x + y);
}

int main() {
    int i = 23;
    f(++i, ++i); // (1)
    f(g(++i), h(++i)); // (2)
    return 0;
}

编辑:

看起来关键点在于编译器是否可以在调用gh之前执行两个增量--根据下面的答案,我的理解是可以的,但我希望确认一下。


2
@MichaelDorgan:我不会的 :) 我正在开发一个静态分析工具,以帮助阻止人们做这种事情,并且这种区别可能很重要。 - Stuart Golodetz
2个回答

12

错误。序列点指定操作的允许顺序的部分顺序。在情况(2)中,有以下序列点:

  1. 在行(1)末尾的分号后
  2. 在评估g的参数(即++i)之后,但在调用g之前
  3. 在评估h的参数(即++i)之后,但在调用h之前
  4. 在评估f的参数之后(即在fg返回之后),但在调用f之前
  5. 在从f返回后

因此,这个部分顺序如下:

    1
   / \
  /   \
 2     3
  \   /
   \ /
    4
    |
    | 
    5

相对于彼此,2和3没有顺序,因为参数的评估顺序是未指定的。由于在序列点1和4之间,i被修改了两次,所以行为是未定义的。


啊,好的,我很高兴检查了一下!我知道序列点是一个偏序,但我的想法是这样的——由于参数求值的顺序是未指定的,你会得到1->2->3->4->5或者1->3->2->4->5。在任何情况下,我认为在1和4之间有一个序列点(在2或3处)。看来我完全错了。 - Stuart Golodetz
1
我不反对这种方法,但我认为它并不立即遵循标准;6.5.2规定一个对象在相邻的序列点之间最多只能被修改一次,但上述图中的1和4是非相邻的。实现是否需要表现得好像序列点图被线性化了? - ecatmur
@ecatmur:嗯,说得对。我看了一下标准,也没太明白。C99 §6.5/2说:“在前一个和下一个序列点之间……”,但是在#1之后不清楚“下一个序列点”是什么——gh的求值顺序未指定,它们在调用之前评估其参数并具有一个序列点,这两个序列点发生在#4之前。 - Adam Rosenfield
@AdamRosenfield:这就是我对此的误解的关键所在 - 如果g(++i)整个被评估,或者h(++i)整个被评估,那么在任一情况下,在两个增量之间将会有一个序列点,行为似乎不会是未定义的(尽管在正式意义上可能是)。但是,如果增量都可以在调用gh之前发生,那么行为显然是未定义的而非未指定的。我当前的理解是这就是关键点。 - Stuart Golodetz
@Stuart:虽然标准没有明确说明,但我想它可能被解释为“如果有一种允许的副作用和序列点排序方式会导致对象在序列点之间被多次修改,则行为是未定义的”?因为使用这种措辞,行为显然是未定义的,因为编译器可以在调用gh之前将两个++i都放置在其之前(尽管不需要这样做)。 - Adam Rosenfield
@AdamRosenfield:我完全同意,如果有这样的措辞,并且增量明确可以在调用gh之前发生,那么行为显然是未定义的。我认为,只要两个增量都可以首先发生(即我愿意假设您提供的解释),它就是未定义的。我不清楚的是(也是我工作中的本地辩论所在),在哪里声明增量可以在两个调用之一被调用之前都发生。 - Stuart Golodetz

2

根据6.5.2.2 10条款,子表达式参数在实际调用之前的评估中没有顺序点。

有一种看法是,无论行为是否未定义,都是未指定的;如果实现对++i子表达式进行排序,然后调用gh,则行为是未定义的,但如果尽可能晚地评估++i子表达式(即直接在分别调用gh之前),则行为是未指定的。然而,由于实现始终可以在任何允许的未指定行为之间进行选择,因此总体结果是未定义的。


没错 - 所以在调用 gh 之前,两个 ++i 可能都会发生,这种情况下就没有顺序点的限制,因此结果是未定义的。 - Stuart Golodetz
5
我认为在 C 语言中并不存在“未指定行为是否未定义”的说法。这里的行为是未定义的。 - ouah
我认为尽管术语可能有些不太准确,但我会给出答案,因为它实际上帮助了我更好地思考这个问题(我也点赞了两个回答,因为它们都很有帮助)。不过,如果术语能够更加精练,对未来的读者可能会更有帮助。 - Stuart Golodetz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接