将数组作为引用传递

5
在C++中,当我不知道编译时数组大小时,如何将数组作为引用传递?到目前为止,我发现唯一使它工作的方法是使用类似于下面的东西:
const double( &numbers ) [size]

但这意味着我需要在编译时知道数组的大小,因此我无法在外部函数中使用它。
我的问题是:
1.如果我不将数组作为(const double(& numbers)[length])传递,因为例如我不知道它的大小,那么我如何确保它不会被复制,而是被引用? 2.如果我像上面的例子一样传递一个数组,(double array[])被引用还是被复制
6个回答

10

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

void f( double const numbers[], std::size_t length )
{ ... }

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

谢谢。我在这里得到了一个非常好的答案,它使用了模板!https://dev59.com/P1zUa4cB1Zd3GeqP4pij#7899264 - hyperknot

6

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

在C/C++中,你不能将数组作为副本传递。数组始终是按引用传递的。

编辑

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


2
“数组总是按引用传递”并不完全正确,数组会衰变为指针,然后该指针按值传递。区别在于类型发生了变化,在函数外部它可以是一个真正的数组(T [10]),而在函数内部它将被视为指针(T*),其中包含一些差异(sizeof 的结果是其中最大的差异)。 - David Rodríguez - dribeas
数组通过引用传递 - 这意味着如果您修改数组内容,它将在函数外部反映出来。这就是我的意思。 - Donotalo
在C++中(而不是C),你可以通过引用传递数组,或者传递指向第一个元素的指针,两种方式的语法分别为:void f( int (&x)[10] ); void g( int *p );并且这两种操作具有不同的语义。请注意,在C++中,“按引用传递”具有特定的含义,并不意味着“可以在外部修改对象”,否则void h( int* )将成为“按引用传递int”。这句话是因为我已经读了很多遍,而且我看到有人因此感到困惑。数组没有被复制,但也不是通过引用传递的,而是“衰减”,即传递了指针的值。 - David Rodríguez - dribeas
@davidrodríguez-dribeas: 我编辑了我的回答。我不知道在C++中数组也可以通过引用传递。谢谢。 - Donotalo

3
如果我不将数组传递为(const double(&numbers)[length]),例如我不知道其大小,如何确保它不会被复制,而是被引用?
是的,这意味着您正在作为引用传递数组。
void Foo(const double( &numbers ) [length]);

请注意,length是一个常量整数。

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

不,它没有被复制。这意味着您正在传递指向数组的指针,这等同于:

void Foo(const double *length);

2
一些事情:
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;
}

我的数组大小在编译时已知,但我不能将其硬编码到外部函数文件中。我想要一个代码片段,可以将其保存在类似于“myfunctions.h”的文件中,并在需要计算平均值时随时使用它来处理数组。 - hyperknot
在这种情况下,您应该将它作为指针传递,并使用单独的大小参数。 - Mysticial

2
在C++中,数组的名称只是指向其第一个元素的常量指针。常量指针意味着指针可以更改其指向的任何内容,但它不能被更改为指向其他内容。
这意味着每当您传递一个数组时,实际上是传递该数组的常量指针。换句话说,您已经通过引用传递它,不需要额外的努力。更准确地说,实际上复制的是该常量指针,因此最终(希望不那么令人困惑)的措辞是您正在按值传递数组的常量指针。
如果您在编译时不知道数组的大小,请使用数据类型的(普通)指针而不是显式数组。因此,无论是什么 T my_array[] (其中T是一种类型,如intdouble或甚至是您的某个类),都会变成 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所提到的)。我只是想更详细地阐述这一点。


0

像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的工作方式。在内部,向量是一个具有两个字段的类:指向第一个元素的指针和向量中的元素数量。当您想要接受任何大小的数组作为参数时,这使事情变得更简单:

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

这只是告诉编译器仅接受大小为10的固定数组作为参数。不是这样的。 - curiousguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接