在C中递归期间数组会发生什么?

时间:2014-06-06 19:35:41

标签: c recursion tree tree-traversal

在下面的代码中,我尝试在二叉树中打印从根到叶的所有路径。 如果我写一个递归函数如下:

    void printPath(BinaryTreeNode * n, int path[],int pathlen)
    {
    //assume base case and initializations taken care of

        path[pathlen++] = n->data;
        printPath(root->left,path,pathlen);
        printPath(root->right,path,pathlen);
    }

(我故意删除基本案例和边缘案例处理以提高可读性)

路径数组会发生什么?它只是在每次递归调用期间被修改的一个全局副本吗? pathlen 变量是否会覆盖一些路径值,让人感觉每个堆栈帧都有它&#39 ; 路径的本地副本,因为 pathlen 是每个堆栈帧的本地副本吗?

3 个答案:

答案 0 :(得分:2)

传递int[]变量几乎就像传递int*一样。第一次调用递归函数会传递真实的int[],它只不过是内存中的一个地址,它与每个递归调用中使用的地址相同。

基本上,如果您放置调试打印,例如

printf("%p\n", path);

在你的递归函数中,你会看到地址总是相同的,它不会改变也不会被修改。在调用期间唯一被推入堆栈帧的是数组的地址,但仍然保持不变。

答案 1 :(得分:2)

欢迎使用array-pointer-decay。当你传递一个数组时,会发生两件不同的事情:

  1. 当您声明一个函数来获取一个数组参数时,您实际上是在定义一个带有指针参数的函数。 I. e。,声明

    void foo(int bar[]);
    

    完全等同于

    void foo(int* bar);
    

    数组的声明已经衰减为指向其第一个元素的指针。

  2. 每当你使用一个数组时,它也会衰变成指向它的第一个元素的指针,所以代码

    int baz[7];
    foo(baz);
    

    再次完全等同于

    int baz[7];
    foo(&baz[0]);
    

    只有两个异常,其中不会发生此数组指针衰减:语句sizeof(baz)&baz

  3. 这两个效果一起产生了数组通过引用传递的错觉,即使C只是按值传递指针。


    发明了数组指针衰减,允许根据指针算法定义数组下标运算符:语句baz[3]被定义为等同于*(baz + 3)。尝试将3添加到数组的表达式。但由于数组指针衰减,baz衰减为int*,定义了指针算术,并产生指向数组中第四个元素的指针。然后可以取消引用修改后的指针以获得baz[3]的值。

答案 2 :(得分:1)

void printPath(BinaryTreeNode * n, int path[],int pathlen);

编译器真的像这样看

void printPath(BinaryTreeNode * n, int *path, int pathlen);
  

路径数组会发生什么?是否只是在每次递归调用期间修改的一个全局副本

无。传递相同的path,因为在C数组中传递只是一个指针复制操作;不,它不是一个全局副本,而是一个传递给函数第一次调用的参数,几乎总是存在于堆栈中。

  

并且pathlen变量会覆盖一些路径值,让人觉得每个堆栈帧都有自己的路径本地副本,因为pathlen是每个堆栈帧的本地副本吗?

由于您修改了数组元素的值而不是指向数组开头的指针,因此path本身指向的内容(始终是数组)没有任何变化。就像你说的那样它可能给人一种感觉(特别是如果你已经习惯了其他语言的构造),但实际上只有相同的路径被传递。

旁白:您似乎无法处理退出条件,因为当您开始修改外部元素时,它似乎是一个无限循环,并且可能是未定义的行为数组的界限。