c-递归函数中的多个返回语句



我目前正在阅读"实用C编程第三版";Steve Oualline。

目前,我正在阅读关于递归的第二章,在这一章中,作者提出了一个练习,读者必须尝试编写一个递归函数,计算一个数字在数组中出现的次数。

请考虑以下代码:

#include <stdio.h>
size_t count(const int [], size_t, int);
enum {ARRAY_LEN = 11};
int main()
{
const int num_array[ARRAY_LEN] = {1, 2, 3, 4, 5, 5, 6, 7, 3, 9, 0};
int number = 0;
puts("Type a number: (0-9)");
scanf("%d", &number);
printf("Number of appearances: %lun", count(num_array, ARRAY_LEN, number));
return 0;
}
size_t count(const int n_array[], size_t arr_len, int num)
{
return arr_len == 0 ? 0 : ( n_array[arr_len - 1] == num ) + count( n_array, arr_len - 1, num );
}

我的问题是:

C如何知道将哪个return带回主函数?因为,在这个例子中,我们有3个返回,并且在特定的时间内,所有返回的条件都变为true,那么,C怎么知道有了return 0;,它必须退出递归函数,而不继续在count()函数内搜索另一个返回呢?

谢谢。

当您有递归函数调用时,您有多个"实例";一个函数处于活动状态,相互调用并返回。为了形象化这一点,我发现想象一下很有用,而不是在计算机上运行C程序,你有一群人在一个房间里,你们都给他们一套相同的指令,然后你让他们开始为你解决问题。对于你问题中的代码,它会是这样的。

你挑选一个人,然后说:;嘿,约翰,这是一个3号的数组:[1, 2, 3]。你能告诉我2这个数字在里面出现了多少次吗">

约翰从你手里接过阵法,然后看了看他的指示。数组的大小不是0,所以他不会告诉你0。他的数组的最后一个元素不是2,所以他也不做第二件事。但要做第三件事,他需要进行递归调用,所以他选择房间里的其他人,然后说,";嘿,玛丽,这是一个2:[1, 2]的数组。你能告诉我2这个数字在里面出现了多少次吗">

玛丽从约翰那里接过了这个阵列,她看着自己的指示。数组的大小不是0,所以她没有告诉John 0。但是她的数组的最后一个元素是2,所以她做了第二件事。但要做第二件事,她还需要进行递归调用,所以她选择房间里的其他人,然后说,";嘿,弗雷德,这是一个1:[1]大小的数组。你能告诉我2这个数字在里面出现了多少次吗">

弗雷德看了看他的指示。数组的大小不是0,所以他没有告诉Mary 0。他的数组的最后一个元素不是2,所以他不做第二件事。他也需要进行递归调用来做第三件事,所以他选择房间里的其他人说,";嘿,简,这是一个0:[]大小的数组。你能告诉我2这个数字在里面出现了多少次吗">

Jane看了看她的数组,结果的大小是0。所以她说;则2在该阵列中出现的次数为:0";。

这就是弗雷德一直在等待的。弗雷德正在编写第三份return声明,所以无论他从简那里听到什么,都是他的答案。他说,";Mary,2在该数组中出现的次数为:0";。

这就是玛丽一直在等的。但她正在编写第二个return声明,所以她从弗雷德那里听到的任何消息都加了1。她说,";John,2在该数组中出现的次数为:1〃;。

这就是约翰一直在等的。约翰正在编写第三份return声明,所以无论他从玛丽那里听到什么,都是他的答案。他对你说:;CCD_ 21出现在该阵列中的次数为:1〃;。

现在你有了答案!

我曾经教过一堂C编程课,有一天我给班上的每个人发了一份讲义,里面有一些人类可读的指令,说明如何对二叉树进行递归、有序遍历。然后我递上一个";二叉树"——一块纸板,上面有一堆黄色的贴纸,排列成树状结构——给前排的一个随机学生,开始遍历。这绝对是一片混乱,但也很有趣。(我觉得。不管怎样,我觉得这很有趣!)

CPU有一个堆栈。这是一个非常重要的数据结构,用于描述执行上下文。堆栈可能如下所示:

计数(4,{10,20,30},3)计数(4,{10,99,33,22},4)计数(4,{10,99,33,22,44},5)main(1)

当CPU调用一个函数时,它会在顶部添加另一个堆栈帧(实际上,有些人或调试器会将其倒置,所以它可能在底部)。

当CPU从函数返回时,它从顶部移除堆栈帧,并从现在位于顶部的执行点继续。

如果您可视化这个堆栈,您将看到每个return语句都没有任何明确的目的地——它是告诉CPU在哪里继续执行的堆栈。如果main调用count,则返回main,但如果count调用count,则返回到count。但不仅如此,它还记得返回哪个调用。

堆栈帧包含调用函数时使用的所有参数的副本,以及函数的局部变量的副本(它并不总是副本,但最简单和最重要的情况是它实际上是副本)。

对于初学者来说,带有多个返回语句的函数定义是糟糕的。

此外,函数应该像一样声明

size_t count( const int [], size_t, int );

该功能可以通过以下方式定义

size_t count( const int a[], size_t n, int num )
{
return n == 0 ? 0 : ( a[n-1] == num ) + count( a, n - 1, num );
}

也就是说,函数的返回类型应为size_t。函数的第一个参数应具有限定符const,因为传递给函数的数组在函数内没有更改。

总的来说,你应该写

printf( "Number of appearances: %zun", count( num_array, ARRAY_LEN, number ) );

至于你的问题,很明显,函数中的控制将传递给一个return语句,这取决于if语句中使用的条件。

如果arr_len的值等于0,则控制将传递给该if语句中的return语句。

if(arr_len == 0)
return 0;

请注意,在函数的每次递归调用中,参数arr_len的值都会递减。

因此,当arr_len等于0时,这是函数的基本情况。在这种情况下,函数将停止执行。

否则,如果arr_len不等于0,则如果数组的最后一个元素等于num,则将下一个递归调用返回的值加1,并返回总和。否则,如果数组的最后一个元素不等于num,则返回函数的下一次递归调用返回的值。

if(n_array[arr_len - 1] == num)
return (1 + count(num, n_array, arr_len - 1));
else
return count(num, n_array, arr_len -1);

这就是递归编程的全部内容。

代码使用在if链中遇到的第一个返回。如果该返回递归地调用";计数";则该返回被搁置,直到嵌套的";计数";调用返回。这种情况一直持续到arr_len == 0上的所有内容都已返回。

最新更新