首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C评估转让报表的顺序

C评估转让报表的顺序
EN

Stack Overflow用户
提问于 2016-06-02 16:55:39
回答 2查看 998关注 0票数 5

在一种情况下,我遇到了跨平台代码在基本赋值语句上的不同行为。

一个编译器首先评估Lvalue,然后评估Rvalue,然后评估赋值。

另一个编译器首先执行Rvalue、Lvalue第二,然后执行赋值。

如果Lvalue影响Rvalue的值,这可能会产生影响,如下所示:

代码语言:javascript
复制
struct MM {
    int m;
}
int helper (struct MM** ppmm ) { 
    (*ppmm) = (struct MM *) malloc (sizeof (struct MM)); 
    (*ppmm)->m = 1000;
    return 100;
}

int main() { 
    struct MM mm = {500};
    struct MM* pmm = &mm
    pmm->m = helper(&pmm);
    printf(" %d %d " , mm.m , pmm->m);
}

上面的例子,行pmm->m = helper(&mm);,取决于计算的顺序。如果首先计算Lvalue,则大于pmm->m等于mm.m,如果Rvalue比pmm->m先计算,则等于在堆上分配的MM实例。

我的问题是,是否有C标准来确定计算的顺序(没有找到),或者每个编译器都可以选择做什么。还有其他类似的陷阱我应该知道吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-06-02 18:13:51

计算=表达式的语义包括:

更新左操作数的存储值的副作用是在左操作数和右操作数的值计算之后进行排序。操作数的计算没有排序。

(C2011,6.5.16/3;重点添加)

强调的条款明确地允许您在由不同的编译器编译程序时,观察到程序行为上的差异。此外,除其他外,未排序意味着允许评估按不同的顺序进行,甚至在程序的相同构建的不同运行中也是如此。如果未排序的计算出现的函数被多次调用,则允许在同一程序的执行过程中,在不同的调用期间以不同的顺序进行评估。

这已经回答了这个问题,但重要的是要看到更大的前景。修改对象或调用这样做的函数是一个副作用(C2011,5.1.2.3/2)。因此,这一关键条款发挥了作用:

如果对标量对象的副作用相对于对同一个标量对象的不同副作用或使用同一个标量对象的值计算而言没有排序,则行为是未定义的。

(C2011,6.5/2)

被调用函数的副作用是修改存储在main()变量pmm中的值,对赋值的左操作数的求值需要使用pmm值进行值计算,而这些值是未排序的,因此行为是未定义的。

要不惜一切代价避免未定义的行为。因为您的程序的行为是未定义的,所以并不局限于您观察到的两个备选方案(以防不够糟糕)。C标准对它可能做的事情没有任何限制。相反,它可能会崩溃,清除硬盘的分区表,或者,如果你有合适的硬件,召唤鼻涕恶魔。或者其他任何东西。这些都不太可能,但最好的观点是,如果您的程序有未定义的行为,那么您的程序就是错误的。

票数 5
EN

Stack Overflow用户

发布于 2016-06-02 18:21:56

当使用简单赋值运算符:=时,未指定运算数的计算顺序。在评估之间也没有顺序点。

例如,如果您有两个函数:

代码语言:javascript
复制
*Get() = logf(2.0f);

没有指定在任何时候调用它们的顺序,但是这个行为是完全定义的。

函数调用将引入一个序列点。这将发生在对论点的评估和实际调用之前。运算符;也将引入一个序列点。这一点很重要,因为在没有中间序列点的情况下,不能对对象进行两次修改,否则行为就没有定义。

由于未指定的行为,您的示例特别复杂,并且可能有不同的结果,这取决于首先计算左操作数还是右操作数。

  1. 首先计算左操作数。

计算左操作数,指针pmm将指向结构mm。然后调用函数,就会出现一个序列点。它通过将指针pmm指向已分配的内存来修改指针;,然后是一个序列点,因为操作符;。然后,它将值1000存储到成员m,然后是另一个序列点(因为; )。函数返回100并将其赋值给左操作数,但是由于左操作数是首先计算的值100,所以它被赋值给对象mm,更具体地说,它的成员m

mm->m的值为100,ppm->m的值为1000。这是定义的行为,在序列点之间没有对象被修改两次.

  1. 首先计算右操作数。

函数首先被调用,序列点出现,它通过将指针ppm指向新分配的结构来修改指针,然后是序列点。然后将值1000存储到成员m,然后是序列点。然后函数返回。然后计算左操作数,ppm->m将指向新分配的结构,其成员m将通过赋值100对其进行修改。

mm->m的值为500,因为它从未修改过,而pmm->m的值为100。在序列点之间没有对象被修改两次。行为是定义的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37597855

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档