我正在设计递归算法:
int f(int[] a, int[] b){
----changing a here
----changing b here
f(a,b)
----writing a here
----writing b here
}
我知道所有数组都是指针,所以这段代码应该有问题。在所有递归调用完成后,它将写出a和b的最终值。我不希望这种情况发生。
如何将该数组作为常规“按值传递”传递给递归调用?
PS:在递归调用的任何地方都可以动态分配或调整数组等。
答案 0 :(得分:3)
答案 1 :(得分:3)
我会假设int f(int[] a, int[] b)
是拼写错误:它应该是int f(int a[], int b[])
。
首先,arrays and pointers are different。在许多情况下,数组的名称“衰减”到指向其第一个元素的指针,但并非总是如此。否则,对于数组sizeof a
,a
将无法“正常工作”。
其次,让我们暂时忽略递归。让我们也简化函数原型:
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中的值传递数组。您可以让调用者传递可修改的副本 - 或者您可以让接收者(被调用者?)进行复制;如果接收者不打算进行修改,那么一个或另一个必须如此。