任何像函数的多个实例

时间:2016-12-17 08:30:47

标签: c function recursion stack instance

我正在使用C中的代码进行合并排序,如下所示:

void merge(int a[], int low, int mid, int high);

void divide(int a[], int low, int high)
{
 if(low<high) // The array has atleast 2 elements
 {
  int mid = (low+high)/2;
  divide(a, low, mid);     // Recursion chain to sort first half of the array
  divide(a, mid+1, high);  // Recursion chain to sort second half of the array
  merge(a, low, mid, high);
 }
}

void merge(int a[], int low, int mid, int high)
{
 int i, j, k, m = mid-low+1, n = high-mid;
 int first_half[m], second_half[n];
 for(i=0; i<m; i++)  // Extract first half (already sorted)
   first_half[i] = a[low+i];
 for(i=0; i<n; i++)  // Extract second half (already sorted)
   second_half[i] = a[mid+i+1];
 i = j = 0;
 k = low;
 while(i<m || j<n)  // Merge the two halves 
 {
   if(i >= m)
   {
    a[k++] = second_half[j++];
    continue;
   }
   if(j >= n)
   {
    a[k++] = first_half[i++];
    continue;
   }
   if(first_half[i] < second_half[j])
     a[k++] = first_half[i++];
   else
     a[k++] = second_half[j++];
 }
} 

main()
{
 int i, n, a[10];
 printf("How many elements in the array? ");
 scanf("%d", &n);
 printf("Enter array: ");
 for(i=0; i<n; i++)
   scanf("%d", &a[i]);

 divide(a, 0, n-1);

 printf("\nSorted array:  ");
 for(i=0;i<n;i++)
   printf("%d  ",a[i]);
 printf("\n");
}  `enter code here`

这里函数除法是递归的,它会再次调用自己,直到条件满足为止。但我很困惑是否有任何概念,如&#34;功能实例&#34;或&#34;功能副本&#34;因此,在对该函数进行的不同调用期间,被调用的函数内的变量的值与其值无关。因此,当在此代码中再次调用除数时,数组&#34; a&#34;作为参数传递给函数保持与在初始调用函数中传递的相同,或者是在前一次调用中获得的更改版本? 我想它不应该是相同的原因显然会使对相同函数内的函数的调用毫无意义。 有人可以对它进行一些说明,无论是复制品还是功能实例都会在这里发挥作用?

2 个答案:

答案 0 :(得分:1)

C使用按值调用。这意味着每个函数调用都将参数作为自己的一组值。在您的情况下,3个值将传递给divide,即指向数组的指针和2个整数。对这些值所做的任何更改都是特定函数调用的本地更改。

C标准没有具体说明如何实施。该标准仅描述了它的行为方式。

最常见的实现使用堆栈来存储函数的本地值。堆栈是在启动时为程序(进程/线程)保留的内存区域。堆栈指针用于指示堆栈的当前结束位置。当一个函数被调用时,它会将堆栈指针增加函数所需的字节数来保存局部变量(可能还有其他一些东西,如返回地址等)。当函数返回时,堆栈指针递减相同的量(意味着所有局部变量都丢失,即超出范围)。

因此,当递归调用函数时,每次调用都会将堆栈指针递增一些量。当&#34;条件&#34;如果满足并且函数调用开始返回,则堆栈指针再次递减。

所以简而言之:

函数代码只出现一次,但局部变量在堆栈中出现的次数与嵌套函数调用次数一样多。

注意:在上面的文本中,我在进行函数调用时写入增量,在函数返回时减少。然而,实现定义了堆栈增长的方向。因此,它可能会减少调用和递增回报。

示例代码:

#include <stdio.h>
 
void foo(int n)
{
    printf("n=%d at address %p\n", n, (void*)&n);
    if (n == 0) return;
    foo(n-1);
}
 
int main(void) {
    foo(5);
    return 0;
}

在一个特定系统上输出:

n=5 at address 0xfff6c220
n=4 at address 0xfff6c200
n=3 at address 0xfff6c1e0
n=2 at address 0xfff6c1c0
n=1 at address 0xfff6c1a0
n=0 at address 0xfff6c180

因此,此示例显示了局部变量n如何在堆栈上结束。您可以看到,在此系统上,n被放置在每个函数调用的不同内存位置。您还可以看到距离是0x20(十进制32),表明每次调用堆栈指针都会更改32。它还表明堆栈在此系统上向下增长。

答案 1 :(得分:0)

尽管有数组语法(int a[]),但实际上只传递了指向数组第一个元素的指针。因此,在调用期间没有原始数组的副本。指针通常会在递归调用中传递,因此会有指针的副本。

但是,编译器可能(不保证)在divide()调用自身的地方替换divide()的正文。它可以在替代代码中再次执行。显然,如果事先不知道递归的深度,就不能无限次地(不可能)或太多次(生成的代码太多)完成。但是可能会像3级深度完成,然后会有第4级的递归调用(另外3次没有调用,然后再次调用,依此类推)。当编译器替换代码并消除调用时,一些其他优化成为可能。在我们的例子中,不需要在3个替换级别内制作指针的副本。