无限循环与无限递归。两者都未定义吗?
Infinite loop vs infinite recursion. Are both undefined?
没有副作用的无限循环是未定义的行为。参见 here for the example from cppreference。更简单的例子:
int foo() {
while(true) {}
return 42;
}
现在考虑几乎等价的
int bar() {
if (true) return bar();
return 42;
}
这是否也会调用未定义的行为?
或者换句话说:什么class错误是无限递归根据语言?
PS:请注意,我在 运行 时知道其中的含义:原则上循环可能 运行 永远,而递归最终将导致计算器溢出。尽管我主要对编译器对它们所做的事情感兴趣。也许是一个很学术的问题...
不,没有区别。 [basic.progress]p1:
The implementation may assume that any thread will eventually do one of the following:
terminate,
make a call to a library I/O function,
perform an access through a volatile glvalue, or
perform a synchronization operation or an atomic operation.
无限循环的方式并不重要;如果它没有执行上述任何一点,您将获得 UB。其中包括:
int bar(int cond) {
if (cond == 42) bar(cond);
return 42;
}
bar(some_user_input);
允许编译器假定 some_user_input
永远不会是 42。
在大多数现实世界的情况下,假设某事会发生的邀请意味着一个人没有义务采取任何特殊措施来考虑它不会发生的可能性,但这并不是任意行为的许可如果发现所讨论的事件不会发生,那么时尚。
然而,根据对 C 标准的流行解释,如果标准将邀请他们做出的任何假设被证明是不正确的,则没有理由期望实现将避免完全荒谬的时尚。
我不认为该标准的作者打算让 C 编译器使用复杂的推理引擎,这些推理引擎将假设级联到编译器给出的点,例如
if (x > 1000) foo(x);
while ( x <= 1000)
somethingWithNoSideEffectsThatCantAffectX();
会得出结论,无论 x
的值如何,foo
都应该无条件执行。更有可能的是,他们打算让编译器给出如下内容:
volatile int yy;
void test(int x, int *restrict arr)
{
int y;
do
x=arr[x];
while(x & 1);
y=yy;
if (y)
printf("%d\n",x);
}
将被允许使用这样一个事实,即可能需要也可能不需要 x
的最终值来重新排列代码,以便在不需要的情况下更有效(通过完全跳过它的计算) ).本质上,一段代码需要时间来执行这一事实——即使无限——本身不应被视为副作用,如果编译器可以证明一段代码在到达某个地方之前不能执行任何可见的副作用,它不需要等待那段代码完成才能在那个地方执行代码。
不幸的是,该标准的作者依赖编译器编写者来识别基于某些假设可以采取哪些有用的操作,而编译器编写者假设他们可以得出的任何可能的推论都应该被假定为有用。
没有副作用的无限循环是未定义的行为。参见 here for the example from cppreference。更简单的例子:
int foo() {
while(true) {}
return 42;
}
现在考虑几乎等价的
int bar() {
if (true) return bar();
return 42;
}
这是否也会调用未定义的行为?
或者换句话说:什么class错误是无限递归根据语言?
PS:请注意,我在 运行 时知道其中的含义:原则上循环可能 运行 永远,而递归最终将导致计算器溢出。尽管我主要对编译器对它们所做的事情感兴趣。也许是一个很学术的问题...
不,没有区别。 [basic.progress]p1:
The implementation may assume that any thread will eventually do one of the following:
terminate,
make a call to a library I/O function,
perform an access through a volatile glvalue, or
perform a synchronization operation or an atomic operation.
无限循环的方式并不重要;如果它没有执行上述任何一点,您将获得 UB。其中包括:
int bar(int cond) {
if (cond == 42) bar(cond);
return 42;
}
bar(some_user_input);
允许编译器假定 some_user_input
永远不会是 42。
在大多数现实世界的情况下,假设某事会发生的邀请意味着一个人没有义务采取任何特殊措施来考虑它不会发生的可能性,但这并不是任意行为的许可如果发现所讨论的事件不会发生,那么时尚。
然而,根据对 C 标准的流行解释,如果标准将邀请他们做出的任何假设被证明是不正确的,则没有理由期望实现将避免完全荒谬的时尚。
我不认为该标准的作者打算让 C 编译器使用复杂的推理引擎,这些推理引擎将假设级联到编译器给出的点,例如
if (x > 1000) foo(x);
while ( x <= 1000)
somethingWithNoSideEffectsThatCantAffectX();
会得出结论,无论 x
的值如何,foo
都应该无条件执行。更有可能的是,他们打算让编译器给出如下内容:
volatile int yy;
void test(int x, int *restrict arr)
{
int y;
do
x=arr[x];
while(x & 1);
y=yy;
if (y)
printf("%d\n",x);
}
将被允许使用这样一个事实,即可能需要也可能不需要 x
的最终值来重新排列代码,以便在不需要的情况下更有效(通过完全跳过它的计算) ).本质上,一段代码需要时间来执行这一事实——即使无限——本身不应被视为副作用,如果编译器可以证明一段代码在到达某个地方之前不能执行任何可见的副作用,它不需要等待那段代码完成才能在那个地方执行代码。
不幸的是,该标准的作者依赖编译器编写者来识别基于某些假设可以采取哪些有用的操作,而编译器编写者假设他们可以得出的任何可能的推论都应该被假定为有用。