10

C 和 C++ 允许将结构和对象按值传递给函数,但阻止按值传递数组。

为什么?

4

6 回答 6

18

在 C/C++ 中,在内部,数组作为指向某个位置的指针传递,基本上,它按值传递的。问题是,复制的值表示同一位置的内存地址。

顺便说一下,在 C++ 中, avector<T>被复制并传递给另一个函数。

于 2009-03-22T14:22:28.203 回答
11

您可以按值传递数组,但您必须首先将其包装在结构或类中。或者干脆使用像 std::vector 这样的类型。

我认为这个决定是为了效率。大多数时候都不想这样做。这与为什么没有无符号双打的原因相同。没有相关的 CPU 指令,因此您必须使效率不高的事情很难用 C++ 之类的语言来完成。

正如@litb 提到的:“C++1x 和 boost 都将原生数组包装到提供 std::array 和 boost::array 的结构中,这是我一直喜欢的,因为它允许在结构中传递和返回数组”

数组是指向包含该数组和大小的内存的指针。请注意,它与指向数组第一个元素的指针并不完全相同。

大多数人认为您必须将数组作为指针传递并将大小指定为单独的参数,但这不是必需的。您可以传递对实际数组本身的引用,同时保持它的 sizeof() 状态。

//Here you need the size because you have reduced 
// your array to an int* pointing to the first element.
void test1(int *x, int size)
{
  assert(sizeof(x) == 4);
}

//This function can take in an array of size 10
void test2(int (&x)[10])
{
  assert(sizeof(x) == 40);
}

//Same as test2 but by pointer
void test3(int (*x)[10])
{
  assert(sizeof(*x) == 40);
  //Note to access elements you need to do: (*x)[i]
}

有人可能会说数组的大小是未知的。这不是真的。

int x[10];  
assert(sizeof(x) == 40);

但是堆上的分配呢?堆上的分配不返回数组。它们返回指向数组第一个元素的指针。所以 new 不是类型安全的。如果您确实有一个数组变量,那么您将知道它所包含的大小。

于 2009-03-22T14:18:37.930 回答
8

编辑:我在下面留下了原始答案,但我相信大部分价值现在都在评论中。我已将其设为社区 wiki,因此如果参与后续对话的任何人想要编辑答案以反映该信息,请随意。

原始答案

一方面,它如何知道要分配多少堆栈?这对于结构和对象(我相信)是固定的,但是对于数组,它将取决于数组的大小,直到执行时才知道。(即使每个调用者在编译时都知道,也可能有不同的调用者具有不同的数组大小。)您可以在参数声明中强制使用特定的数组大小,但这似乎有点奇怪。

除此之外,正如布赖恩所说,还有效率问题。

你想通过所有这些实现什么?是否要确保原始数组的内容没有改变?

于 2009-03-22T14:21:56.333 回答
5

我认为数组在 C 中作为指针而不是按值传递的主要原因有 3 个。其他答案中提到了前两个:

  • 效率
  • 因为通常没有数组的大小信息(如果包含动态分配的数组)

但是,我认为第三个原因是由于:

  • C 从 B 和 BCPL 等早期语言演变而来,其中数组实际上是作为指向数组数据的指针实现的

Dennis Ritchie 谈到了 C 从 BCPL 和 B 等语言的早期演变,特别是数组是如何实现的,它们如何受到 BCPL 和 B 数组的影响,以及它们如何以及为什么不同(同时在表达式中保持非常相似,因为数组名称会衰减到表达式中的指针)。

于 2009-03-22T17:41:55.883 回答
3

这是那些“只是因为”的答案之一。C++ 从 C 继承了它,并且必须遵循它以保持兼容性。为了提高效率,它在 C 中是这样做的。您很少希望在堆栈上复制一个大型数组(请记住,这里是 PDP-11)以将其传递给函数。

于 2009-03-22T16:42:45.307 回答
3

我实际上并不知道任何支持按值传递数组的语言。这样做不会特别有用,并且会很快占用调用堆栈。

编辑:对downvoters - 如果你知道更好,请让我们都知道。

于 2009-03-22T14:27:27.473 回答