48

下面是《 C Programming Just the FAQs》一书中的一个片段。这不是错误的,因为数组永远不能按值传递吗?

VIII.6:如何将数组按值传递给函数?

答:数组可以通过值传递给函数,方法是在被调用函数中声明数组名称,并在末尾附加方括号([和)。]调用函数时,只需将数组的地址(即数组的名称)传递给被调用函数即可。例如,以下程序将数组传递 x[]给按值命名的函数 byval_func()

int[]参数告诉编译器该byval_func() 函数将采用一个参数——一个整数数组。调用该 byval_func()函数时,您将数组的地址传递给 byval_func()

byval_func(x);

因为数组是按值传递的,所以会制作数组的精确副本并将其放置在堆栈上。然后被调用的函数接收这个数组的副本并可以打印它。因为传递给的数组 byval_func()是原始数组的副本,所以在byval_func()函数内修改数组对原始数组没有影响。

4

6 回答 6

73

因为数组是按值传递的,所以会制作数组的精确副本并将其放置在堆栈上。

这是不正确的:数组本身没有被复制,只有指向其地址的指针的副本被传递给被调用者(放置在堆栈上)。(无论您是否将参数声明为int[]int*,它都会衰减为指针。)这允许您从被调用函数中修改数组的内容。因此,这

因为传递给的数组byval_func()是原始数组的副本,所以在byval_func()函数内修改数组对原始数组没有影响。

完全错误(感谢@Jonathan Leffler 在下面的评论)。但是,在函数内部重新分配指针不会改变指向函数外部原始数组的指针。

于 2011-01-23T15:03:17.787 回答
60

烧掉那本书。如果您想要一个不是由初学者编写的真正的 C 常见问题解答,请使用这个:http ://c-faq.com/aryptr/index.html 。

在语法方面,严格来说,您不能在 C 中按值传递数组。

void func (int* x); /* this is a pointer */

void func (int x[]); /* this is a pointer */

void func (int x[10]); /* this is a pointer */

然而,作为记录,C 中有一个肮脏的技巧,它允许您在 C 中按值传递数组。不要在家里尝试这个!因为尽管有这个技巧,仍然没有理由按值传递数组。

typedef struct
{
  int my_array[10];
} Array_by_val;

void func (Array_by_val x);
于 2011-01-23T20:07:16.010 回答
2

这不是错误的,因为数组永远不能按值传递吗?

确切地。您不能在 C 中按值传递数组。

我看了一下书中引用的部分,很快就找到了这种混乱或错误的根源。

作者不知道这*i相当于i[]何时作为参数提供给函数。发明后一种形式是为了明确说明代码的读者,它i指向一个数组,这是一个很大的混淆来源,正如这个问题所展示的那样。

我认为有趣的是,这本书特定部分的作者或其他部分中的至少一个(因为这本书总共有5位作者)或7位校对者中的一位至少没有提到这句话:

"byval_func()函数被调用时,你将数组的地址byval_func()传递给: "

至少,他们应该注意到有冲突。由于您传递了一个地址,因此它只是一个地址。没有什么神奇的事情可以将地址变成一个全新的数组。


但回到问题本身:

您不能在 C 中按值传递数组,因为您似乎已经了解自己。但是您可以做三件事(可能还有更多,但这是我的实际情况),这可能是根据独特情况的替代方案,所以让我们开始吧。

  1. 将数组封装在结构中(如其他答案所述):
#include <stdio.h>

struct a_s {
   int a[20];
};

void foo (struct a_s a)
{
   size_t length = sizeof a.a / sizeof *a.a;

   for(size_t i = 0; i < length; i++)
   {
       printf("%d\n",a.a[i]);
   }
}

int main()
{
   struct a_s array;

   size_t length = sizeof array.a / sizeof *array.a;

   for(size_t i = 0; i < length; i++)
   {
       array.a[i] = 15;
   } 

   foo(array);
}
  1. 通过指针传递,还要添加一个参数来确定数组的大小。在被调用的函数中,使用该大小信息创建了一个新数组,并分配了调用者中数组中的值:
#include <stdio.h>

void foo (int *array, size_t length)
{
   int b[length];

   for(size_t i = 0; i < length; i++)
   {
       b[i] = array[i];
       printf("%d\n",b[i]);
   }
}

int main()
{
   int a[10] = {0,1,2,3,4,5,6,7,8,9};

   foo(a,(sizeof a / sizeof *a));
}
  1. 避免定义本地数组,只使用一个具有全局范围的数组:
#include <stdio.h>

int a[10];
size_t length = sizeof a / sizeof *a;

void foo (void)
{
   for(size_t i = 0; i < length; i++)
   {
       printf("%d\n",a[i]);
   }
}

int main()
{   
   for(size_t i = 0; i < length; i++)
   {
       a[i] = 25;
   } 

   foo();
}
于 2020-03-09T13:33:44.993 回答
1

在 C 和 C++ 中,不可能通过值将完整的内存块作为参数传递给函数,但我们可以传递它的地址。在实践中,这具有几乎相同的效果,并且它是一种更快、更有效的操作。

为了安全起见,您可以传递数组大小或将 const 限定符放在指针之前,以确保被调用者不会更改它。

于 2012-06-13T15:11:29.507 回答
0

Yuo 可以通过将数组包装到结构中来解决它

#include <stdint.h>
#include <stdio.h>

struct wrap
{
    int x[1000];
};

struct wrap foo(struct wrap x)
{
    struct wrap y;

    for(int index = 0; index < 1000; index ++)
        y.x[index] = x.x[index] * x.x[index];
    return y;
}

int main ()
{
    struct wrap y;

    for(int index = 0; index < 1000; index ++)
        y.x[index] = rand();
    y = foo(y);
    for(int index = 0; index < 1000; index ++)
    {
        printf("%d %s", y.x[index], !(index % 30) ? "\n" : "");
    }


}
于 2020-03-09T12:36:32.363 回答
-15
#include<stdio.h>
void  fun(int a[],int n);
int main()
{
    int a[5]={1,2,3,4,5};
    fun(a,5);
}
void  fun(int a[],int n)
{
    int i;
    for(i=0;i<=n-1;i++)
        printf("value=%d\n",a[i]);
}

通过这种方法,我们可以按值传递数组,但实际上数组是通过其实际复制到堆栈中的基地址来访问的。

于 2013-06-10T06:04:11.963 回答