将C程序确定化的最简单方法

4

请考虑下面的C程序:

#include <stdio.h>

int f() {
  printf("f");
  return 1;
}

int g() {
  printf("g");
  return 2;
}

int main() {
  return f() + g();
}

根据C标准,由于“main”函数中的总和包含两个子表达式,并且来自C99标准的以下摘录:“§6.5(…)未指定子表达式的求值顺序以及副作用发生的顺序”,因此该程序没有单一确定性行为。因此,打印“fg”和“gf”都是该程序的有效输出。实际上,给定的编译器将选择一个固定的求值顺序(例如,在这种情况下,gcc的求值顺序是从左到右),但是如果我想可靠地比较不同编译器之间的输出,则需要确保我的程序具有单一定义行为。我的问题是:最简单的方法是什么?有没有一种方法可以避免使用临时变量(例如,“int tmp = f();return tmp + g();”)?

1
这是一个相当学术的练习。在实践中,您不会对执行此类输出的函数的结果进行求和(或乘法、减法等)。 - Fred Foo
3
同时,编译器的求值顺序不一定是固定的。如果编译器可以通过重新排列操作来进行优化,那么它会这样做。 - Fred Foo
3个回答

2
答案直截了当:避免未指定的行为。
对于您的示例情况,如果main的返回值无关紧要,可以使用以下代码:
return f(), g();

逗号运算符可以确保操作数从左到右执行。
如果您需要f() + g()的值,则使用临时变量是必要的。

@MitchWheat:不,但是OP的要求相当离谱。实际上,你要么这样做,要么使用临时变量来线性化操作。 - Fred Foo
我在这里没有看到未定义的行为。UB意味着可能存在“鼻妖”(http://www.catb.org/jargon/html/N/nasal-demons.html),但这在这里***不是***情况。在此程序中,输出可能正确地是两个可能的输出之一,但永远不会出现鼻妖。 - abelenky
1
@abelenky 所以,"未指定行为"是正确的词语,对吗? - Yu Hao
是的,看起来维基百科对于未指定行为(Unspecified Behavior)的描述比未定义行为(Undefined Behavior)更合适。 - abelenky

1

不使用标准方法,最简单的方式是使用临时变量。


1
为了强制执行评估顺序,您需要在函数调用之间设置一个序列点。
最简单的方法是使用本地变量来存储中间结果。
除非您有深度递归或极其严格的内存限制,否则这很少会成为问题。

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