代码之家  ›  专栏  ›  技术社区  ›  Prasoon Saurav

表达式的行为:定义的还是未定义的?

  •  9
  • Prasoon Saurav  · 技术社区  · 14 年前

    int m[4]={1,2,3,4}, *y; 
    y=m; 
    *y = f(y++); // Expression A
    

    Expression A 他的行为很明确,但我不确定他是否正确。

    根据他的功能 f() sequence point 介于两者之间,因此行为定义良好。

    :我知道我们不应该为了实际目的而编写这样的代码。只是为了学习。:)

    4 回复  |  直到 9 年前
        1
  •  15
  •   James McNellis    14 年前

    如果首先计算左操作数,则 f(y++) 将存储在 m[0] . 如果首先计算右操作数,则结果将存储在 m[1]

    至于行为是否未定义,相关段落为:

    在上一个序列点和下一个序列点之间,通过表达式的求值,对象的存储值最多只能修改一次。此外,先前值应为只读,以确定要存储的值(C99)§6.5/2).

    如果首先计算左侧,那么我们将与第二句冲突,因为顺序是:

    1. y
    2. 价值 y 在右边读取以增加它
    3. y++ 完整且 y 已写入)

    在步骤1中,将 y

        2
  •  13
  •   AnT stands with Russia    14 年前

    关于引入序列点的函数调用,您是完全正确的。但是,该序列点并不能保存案例中的情况。

    首先考虑这个简单的例子

    i = some_function(i++);
    

    有效吗?是的,是的。为什么?它是有效的,因为函数引入的序列点(您正在讨论的那个)分隔了函数的两个修改 i 在没有中间序列点的情况下被修改两次。

    但是,让我们回到您的变体

    *y = f(y++);
    

    在这种情况下,序列点也存在。然而,语言并不能保证评价的顺序 = 运算符(意思是:该语言不保证先计算赋值运算符的哪个操作数:左还是右)。编译器完全可以先计算左侧( *y y++ ),然后调用函数,然后执行实际赋值。在这个潜在的场景中,前两个步骤-读取 y y

        3
  •  1
  •   Loki Astari    14 年前

    表达式定义不明确:

    对该表达式的有效解释是:

    (1) int* t0 = y++; 
    (2) int  t1 = f(t0);
    (3) int& t2 = *y;
    -----------------
    t2 = t1; 
    

    同样有效的解释是:

    (1) int& t2 = *y;
    (2) int* t0 = y++; 
    (3) int  t1 = f(t0);
    -----------------
    t2 = t1; 
    

    这两者都是有效的,并产生不同的结果。所以表达式有未定义的结果。

        4
  •  0
  •   Gian    14 年前

    编辑:这是不正确的,但是我把它留在这里,因为下面的评论中的讨论是有启发性的,我希望是有价值的。

    它是基于C(或C++)中操作符的评价顺序定义的。

    t0 = y++;
    t1 = f(t0);
    *y = t1;
    

    “序列点”这个词有点夸张。序列点并不是真正创建的,它只是为语言定义了严格的求值顺序的结果。

    编辑:虽然这个答案似乎在智力上令人满意,但詹姆斯·麦克内利斯的回答引用了C99规范中的相关部分,该部分指出任务的评估顺序是 定义明确。他确实核实了事实,这是他应得的全部荣誉。我将把我的答案从“它的定义很好”修改为“它可能是关于一个特定编译器的定义很好的”,因为我认为大多数编译器不太可能经常改变它们发出这些代码的顺序(我说“可能”是为了解释任何非常激进的优化)。

    推荐文章