将数组作为引用传递

Passing arrays as a reference

本文关键字:引用 数组      更新时间:2023-10-16

在c++中,当我不知道编译时的大小时,我如何通过数组作为引用 ?到目前为止,我发现让它工作的唯一方法是使用像

这样的东西
const double( &numbers ) [size]

但这意味着我需要知道数组的大小在编译时,因此我不能在外部函数中使用它。

我的问题是:

  1. 如果我不传递一个数组作为( const double( &numbers ) [length] ),因为例如我不知道它的大小,我如何确保它不被复制,但它是引用?
  2. 如果我像上面的例子一样传递一个数组,( double array[] )引用还是复制?

其他答案都很好,但是没有人提到使用模板来处理这个问题。你仍然应该让你的函数接受一个指针和一个大小,但是模板可以自动为你填充:

void f( double const numbers[], std::size_t length )
{ ... }
template< std::size_t Length >
void f( double const (&numbers)[ Length ] )
{
    return f( numbers, Length );
}

在c++中,你应该使用std::vector.

在C/c++中,不能将数组作为副本传递。数组总是通过引用传递。

编辑

在c++中,通过引用传递的数组具有不同的含义。在C和c++中,数组都衰变为指向数组第一个元素的指针。请查看下面的评论。

如果我不传递数组(const double(&numbers) [length]),因为我不知道它的大小,我怎么确保它不会被复制,但会被引用?

是的,这意味着你传递了一个数组作为引用,

void Foo(const double( &numbers ) [length]);

注意length是一个常数整数。

如果我像上面的例子一样传递一个数组,(double array[])就是它引用还是复制?

不,它不被复制。这意味着你要传递一个指向数组的指针也就是,

void Foo(const double *length);

两件事:

  1. c++不允许可变大小的数组。因此,所有数组在编译时都需要有已知的大小。所以我不完全确定如果你最初的问题是适用的,因为你将无法使一个未知大小的数组在第一时间。

  2. 当你传递一个数组时,它是通过引用完成的。

在任何情况下,您可能需要考虑使用vector

编辑:见注释

double average(const double *arr, size_t len){
    //  Compute average
    return accumulate(arr, arr + len, 0) / (double)len;
}
int main(){
    double array[10] = //  Initialize it
    cout << average(array, 10) << endl;
    //  Alternatively: This could probably be made a macro.
    //  But be careful though since the function can still take a pointer instead
    //  of an array.
    cout << average(array, sizeof(array) / sizeof(double)) << endl;
    return 0;
}

在c++中,数组的名称只是指向其第一个元素的常量指针。常量指针意味着一个指针可以改变它所指向的对象,但不能改变它指向别的对象。

这意味着无论何时你传递一个数组,你实际上传递了一个常量指针到该数组。换句话说,您已经通过引用传递了它,不需要额外的努力。更准确地说,实际上复制的是常量指针,所以最后(希望没有那么令人困惑)的措辞是,您将常量指针通过值传递给数组

如果你在编译时不知道数组的大小,只需使用一个(普通的)指针指向你的数据类型,而不是一个显式的数组。所以无论T my_array[]是什么(其中T是一个类型,如int, double甚至是你的一个类)变成T* my_array,语法是完全相同的…my_array[i]可以很好地工作(也存在另一种语法,但没有那么优雅)。对于初始化,使用new操作符:

T* my_array;
my_array = new T[3];

T* my_array;
my_array = new T[x];

,其中x是一个整数(不像普通数组那样是常量)。这样,你就可以在运行时从用户那里获取x,然后创建你的"数组"。在使用完delete[] my_array后,请注意不要忘记它,以避免内存泄漏。

使用这样的动态分配的数组是一个很好的选择,只有当你确切地知道你想要多少个元素…要么在编译时,要么在运行时。因此,例如,如果在用户提供他的x之后,您将完全使用这些,这很好。否则,您将面临数组溢出的危险(如果您需要超过x)—这通常会导致应用程序崩溃—或者只是浪费一些空间。但即使是这种情况,您也需要自己实现数组操作所需的大部分函数。这就是为什么最好使用c++标准库提供的容器,如std::vector(如Donotalo所提到的)。

Java和Python等其他语言在运行时存储数组的长度。在c++数组中,不存储数组的长度。这意味着您需要手动将其存储在某个地方。

当你的代码中有一个固定大小的数组时,编译器知道数组的大小,因为它从源代码本身读取数组。但是一旦代码被编译,长度信息就丢失了。例如:

void f1(double array[10]) {...}

编译器不会强制数组的大小。以下代码将静默编译,因为f1的数组形参只是指向数组第一个元素的指针:

void h1() {
    double a[10];
    double b[5];
    f1(a); // OK.
    f2(b); // Also OK.
}

由于编译器在将数组传递给函数时忽略了数组的静态大小,因此必须知道作为引用传递的任意大小数组的大小的唯一方法是显式声明其大小:

void f2(double array[], size_t array_size) {...}

你可以用任意数组调用这个函数:

void h2() {
    double a[10];
    double b[19];
    f2(a, sizeof(a) / sizeof(a[0]));
    f2(b, sizeof(a) / sizeof(a[0]));
}

参数array_size包含数组的实际大小。

注意,sizeof(array)只适用于静态定义的数组。当您将该数组传递给另一个函数时,大小信息将丢失。

数组不同于指针。然而,像f2的形参这样大小未定义的数组只是指向序列中double的第一个元素的指针:

void f3(double array*, size_t array_size) {...}

对于任何实际用途,f2f3是等效的。

这正是std::vector的工作原理。在内部,vector是一个具有两个字段的类:指向第一个元素的指针,以及vector中元素的数量。当您想要接受任意大小的数组作为参数时,这会使事情变得简单一些:

void g(std::vector& v) {...}