在递归调用中列出参数(C)

时间:2010-02-05 00:10:13

标签: c list arrays recursion

我正在设计递归算法:

int f(int[] a, int[] b){
----changing a here
----changing b here
f(a,b)
----writing a here
----writing b here
}

我知道所有数组都是指针,所以这段代码应该有问题。在所有递归调用完成后,它将写出a和b的最终值。我不希望这种情况发生。

如何将该数组作为常规“按值传递”传递给递归调用?

PS:在递归调用的任何地方都可以动态分配或调整数组等。

7 个答案:

答案 0 :(得分:3)

答案 1 :(得分:3)

我会假设int f(int[] a, int[] b)是拼写错误:它应该是int f(int a[], int b[])

首先,arrays and pointers are different。在许多情况下,数组的名称“衰减”到指向其第一个元素的指针,但并非总是如此。否则,对于数组sizeof aa将无法“正常工作”。

其次,让我们暂时忽略递归。让我们也简化函数原型:

int g(int *a);

(我将int a[]更改为int *a,因为在此上下文中,数组的名称等同于指针。)

现在,您说您可以在函数调用中“动态分配或调整大小”数组。自everything is passed by value in C起,调用者无法看到a g()的动态分配或调整大小:调用方仍具有旧值{{1 }}。但更重要的是,如果a的“原始”类型是一个数组,即调用者的代码如下:

a

然后尝试在int data[SIZE]; g(data); 中调整data的大小是不好的,因为调用中指定的参数没有动态分配到开头。您只能动态调整由g()malloc()calloc()生成的内容。

因此,即使有这个简单的定义,也存在两个问题:

  • 如果realloc()必须动态调整其给定地址所引用的内存大小,则该值必须来自动态分配,而不是数组,
  • 修复之后,由于g()希望能够向调用者发出更改信号,因此需要传递指向需要更改的内容的指针。因此,g()的原型变为:g()

希望以上内容可以帮助您入门。如果你告诉我们更多关于你的算法的信息,特别是你的意思是:“改变”和“写作”,你会得到更好的回应。

编辑:在评论中回答您的问题:

  

因此,当我将一个数组传递给一个函数时,它会衰减到一个指针,并且该指针会按值传递。如果该指针指向我在该调用之前分配的位置......

如果你在调用之前分配了一些内容,那么它永远不会是一个数组。它可以被索引为数组,但这不是重点。那么,也许你对这里的术语感到困惑?

  

...当我的新函数更改指向的值时,该值也会在调用者处更改。

指向的值已更改,是的。

  

我不希望它像这样,所以我需要新函数中指向值的副本,以便我的原始指针的指向值不会改变。我现在清楚了吗?

现在更清楚,但随后提出了更多问题:

  • 如果要在每次调用函数时动态分配或调整数据大小,您将如何返回这些新指针?你不能。这意味着你有自己的内存泄漏。除非你int g(int **a);(递归调用)函数本身的数据。
  • 你会如何调整指针的大小?除非使用标记值,否则您可能无法知道所指向数据的大小。

您是否正在使用该功能迭代解决难题或问题?你是否在每次调用函数时都free()了你的数据?如果你能告诉我们,你到底想要做什么?

答案 2 :(得分:1)

假设您希望输出的顺序与原始顺序相同,如果您不希望外部调用看到在递归调用中对数组所做的更改,则需要复制它们。最简单的方法是将它们分配到堆栈然后从参数进行memcopy,尽管如果它变得太大会导致SO。第二个最简单的是malloc并释放它们。您可能还需要传递数组大小。您无法在C中的堆栈上按值传递数组。

答案 3 :(得分:0)

将它们传递到下一级递归之前,只需打印它们

答案 4 :(得分:0)

你的问题并不十分明确,但我正在读的是:

您有一个递归算法,可以在两个堆分配的数组上运行。其中一个递归调用可能必须重新分配数组,因此当它返回到下一个级别时,旧指针将不再有效。您想知道如何“按值传递数组”以避免这种情况。

首先,您不能在C. Period中按值传递数组。数组总是作为指向第一个元素的指针传递,并且没有办法解决这个问题。

其次,答案总是另一层次的间接。 :)

为了解决在递归调用期间必须重新分配数组的问题,你要做的是让函数采用两个指向指针(int**),其中*a给出指向A数组第一个元素的指针的地址,(*a)[n]给出数组的第n个元素。这样,您可以将数组重新分配给您的心脏内容(更改*a的值),但a本身的值始终保持不变。通过执行此操作,调用堆栈中的函数实例将自动继承递归调用所做的重新分配,因为它们传递给指针(*a)的指向值(a)修改了递归调用以反映实际数据的新位置。

e.g。

int f(int** a, int** b)
{

  /* Do stuff on arrays using (*a)[n] and (*b)[n] */

  if (some_condition) {
     /* Time to reallocate */
     *a = realloc(*a, new_size);
     *b = realloc(*b, new_size);
  }

  /* Do stuff on arrays using (*a)[n] and (*b)[n] */

  f(a, b);  /* Recursive call */

  /* Do more stuff using (*a)[n] and (*b)[n] */
  /* a and b are still valid pointers, even though *a and *b might point somewhere new */

 return foo;
}

void use_f(void)
{
   int* a = malloc(starting_size);
   int* b = malloc(starting_size);

   f(&a, &b);
}

答案 5 :(得分:0)

最简单且最安全的选项是传递指针和大小。假设你正在做类似快速排序的事情:


void sort_range( int* ptr, size_t count )
{
    size_t pivot;

    assert( ptr ); /* make sure we have memory */

    if ( count < 2 ) return; /* terminate recursion */

    pivot = partition( count ); /* select a pivot */

    assert( pivot < count ); /* make sure we don't overrun the buffer */

    sort_range( ptr, pivot ); /* sort left part */
    sort_range( ptr + pivot, count - pivot ); /* sort right part */

    merge( ptr, count, pivot ); /* merge ranges */
}

始终关注您正在使用的内存块的大小。不幸的是,C并不容易,所以你必须养成检查记忆范围的习惯。

答案 6 :(得分:0)

鉴于要求:

int f(int[] a, int[] b){
----changing a here
----changing b here
f(a,b)
----writing a here
----writing b here
}
  

如何将该数组作为常规“按值传递”传递给递归调用?

     

PS:在递归调用的任何地方都可以动态分配或调整数组等。

f()中的代码被授权对数组进行更改(如现在),或者无权进行更改。如果它被授权进行更改,那么除了担心在使用动态分配时是否要泄漏内存之外,没有什么需要做的。

如果代码无权更改数组,则必须复制数组。您可以通过包含适当的const限定符来阻止代码随意修改它们:

int f(const int *a, const int *b) { ... }

请注意,您无法通过C中的值传递数组。您可以让调用者传递可修改的副本 - 或者您可以让接收者(被调用者?)进行复制;如果接收者不打算进行修改,那么一个或另一个必须如此。