我曾经以为在C99中,即使函数fg的副作用受到干扰,尽管表达式f() + g()不包含序列点,但fg会包含一些点,因此行为将不确定:f )将在g()之前调用,或在f()之前调用g()。

我不再那么确定。如果编译器内联函数(即使未声明函数inline,编译器可能会决定这样做)然后重新排序指令怎么办?可以得到与以上两个不同的结果吗?换句话说,这是未定义的行为吗?

这并不是因为我打算写这种东西,而是为了在静态分析器中为这样的语句选择最佳标签。

最佳答案

表达式f() + g()至少包含4个序列点;在f()调用之前一个(在对它的所有零个参数求值之后);在g()调用之前一个(在对它的所有零个参数求值之后);一个作为对f()的调用返回;并返回对g()的调用。此外,与f()关联的两个序列点出现在与g()关联的两个序列点之前或之后。您无法确定序列点将以什么顺序出现-f点是出现在g点之前还是相反。

即使编译器内联了代码,它也必须遵守“好像”规则-代码必须表现出与未交织函数相同的行为。这就限制了损坏的范围(假设编译器为非 buggy )。

因此,未指定计算f()g()的顺序。但是其他一切都很干净。

在评论中,supercat问:



我相信“好像”规则适用,并且编译器没有多余的余地来省略序列点,因为它使用了显式的inline函数。认为(懒得在标准中寻找确切的措词)的主要原因是允许编译器根据其规则内联或不内联函数,但是程序的行为不应更改(除非性能)。


  • 显然,a在b之前执行,而c在d之前执行。我相信c和d在a和b之间执行是可能的,尽管编译器生成这样的代码的可能性很小。类似地,可以在c和d之间执行a和b。尽管我在'c和d'中使用了'and',但这可能是'or'-也就是说,这些操作序列中的任何一个都满足约束:
  • 绝对允许
  • abcd
  • cdab
  • 可能允许(保留a≺b,c≺d顺序)
  • acbd
  • acdb
  • cadb
  • cabd

  • 我相信这涵盖了所有可能的顺序。另请参见chat between Jonathan Leffler and AnArrayOfFunctions-要点是AnArrayOfFunctions根本不考虑“可能允许”的序列。


    内联函数和宏之间存在显着差异,但我认为表达式中的顺序不是其中之一。即,函数a,b,c或d中的任何一个都可以用宏替换,并且可以对宏主体进行相同的排序。在我看来,主要区别在于,使用内联函数,在函数调用(如主答案中概述)以及逗号运算符处都有保证的顺序点。使用宏,您将失去与功能相关的顺序点。 (因此,也许这是一个很大的不同...)但是,在很多方面,这个问题就像是关于多少个天使可以在一根针上跳舞的问题-在实践中并不是很重要。如果有人在代码审查中向我展示了(a(),b()) + (c(),d())表达式,我会告诉他们重写代码以使其清楚:
    a();
    c();
    x = b() + d();
    

    并假设对b()d()没有严格的排序要求。

    关于c - 在C99中,f()+ g()是未定义的还是仅仅是未指定的?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3951017/

    10-12 23:29