递归调用函数时出现分段错误

4

我的任务是创建一个函数,该函数应该计算输入的arcsin。

我尝试使用xcode进行调试。一切都很顺利,直到调用arcsin(new);返回时出现了segmentation fault: 11。我不确定为什么,但在第二个循环运行时,在float arcsin(floatvalue){ ... }处设置断点告诉我float old和float value是NAN

float arcsin(float value){

     float old = value;
     float new = value + (0.5 * ((value * value * value)/3));
     float accurate = 0.00001;  

     if ((new - old) < accurate){
        return new;
     }

     else{
        return arcsin(new);
     }
}


int function_arcsin(int sigdig, float value){

    value = arcsin(value);
    printf("%.10e\n",value);

    return 0;
}

new是一个保留关键字。请将其命名为类似于float newval的名称。 - spicavigo
4
那是C语言,不是C++,所以没问题。 - bitmask
@rojcyk:请问您能否添加一下该函数被调用的位置?它是否在循环中?请添加一些细节。 - Ivan
@rojcyk:你实现arcsin递归的特定原因是什么?这可以(而且应该)用迭代方式完成,除非你想赌编译器是否能够消除尾递归。 - bitmask
2
也许你的公式有问题?在我的机器上,它在第174510次调用自身时崩溃了。 - Matvey Aksenov
显示剩余7条评论
3个回答

5

当调用堆栈过大时,即递归层数过多时,会发生段错误。

在您的情况下,这意味着条件 (new - old) < accurate 将始终计算为 false - 或许不总是如此,但足以使调用堆栈膨胀。

通过测试您的代码,我发现new(可能不是一个好的变量名选择)会不断增长,直到超出 float 的限制。您的算法可能是错误的。


“new”这个名称让我有点困惑,因为它是C++中的一个关键字,但在C语言中算是一个不好的选择吗?(我对C语言的规则不是很熟悉了这些天)。 - user180247
1
@Steve314 你永远不知道什么时候你想要将你的代码移植或者包含到C++中,现在选择好也比以后重构要好,对吧? - Luchian Grigore
1
@Steve314,像newold这样缺乏信息的变量名称在任何语言中都应该被视为糟糕的选择。 - Matvey Aksenov
@LuchianGrigore 感谢您的反馈,我会改进我的变量命名。我也会研究一下算法。再次感谢! - rojcyk
@maksenov - 我在某种程度上同意,但这个函数非常小,在上下文中意义是清晰的。 即使是value的含义(比oldnew不太具体)在上下文中也可能没问题。 对于像这样显而易见的单一参数,我的习惯是将其称为p。 我同意早期养成良好习惯很重要,而且也许更容易改掉过长和明确的名称的习惯,而比起改掉名称过短和不够清晰的习惯来说,你从这个角度看也许是对的。 - user180247

0

我测试了您的程序,发现它一直在无限循环:

((new - old) < accurate)  // never is true

如果您尝试使用大于0的数字,将在10次迭代中达到NaN。对于小于0的数字,会持续进行数千次并导致过深的递归。


0

我相信分段错误是由于递归过深引起的。虽然许多编译器可以将大量递归代码优化为迭代代码,但有些不能这样做,在例如调试选项禁用此功能的情况下很常见。

转换为迭代形式将停止分段错误,但除非我猜错了,否则会导致无限循环。我不希望一个工作的递归解决方案成为这里的问题,除非您正在测试超出逼近收敛范围的值 - 在这种情况下,我首先猜测范围在-pi到+pi之间的输入对于任何可用的arcsin逼近都应该没问题。

我不熟悉arcsin的迭代近似方法,我的谷歌寻找还没有找到答案,但我怀疑您在float new = ...行的计算有误。

我找到了这个链接...

Link

它并没有什么用 - 您的代码既不暗示其中任何一种方法。


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