hyperknot
hyperknot

Reputation: 13966

Passing arrays as a reference

In C++ how can I pass an array as a reference when I don't know the size at compile time? So far, I found out that the only way to make it work is to use something like

const double( &numbers ) [size]

but it means that I need to know the size of the array at compile time, thus I cannot use it in an external function.

My questions are:

  1. If I don't pass an array as a ( const double( &numbers ) [length] ), because for example I don't know its size, how do I make sure that it doesn't get copied, but it is referenced?
  2. If I pass an array like in the above example, ( double array[] ) is it referenced or is it copied?

Upvotes: 5

Views: 25899

Answers (6)

OmarOthman
OmarOthman

Reputation: 1728

In C++, an array's name is just a constant pointer to its first element. A constant pointer means a pointer that's capable of changing whatever it points to, but it can't be changed to point to something else.

This means that whenever you pass an array, you're actually passing a constant pointer to that array. In other words, you're already passing it by reference, no need for extra efforts. To be more accurate, what is actually copied is that constant pointer, so the final (hopefully not-that-confusing) phrasing is that you're passing a constant pointer to the array by value.

If you don't know your array's size at compile time, just use a (normal) pointer to your data type instead of an explicit array. So whatever is T my_array[] (where T is a type, like int, double or even one of your classes) becomes T* my_array, and the syntax is exactly the same hereafter... my_array[i] will work fine (another syntax also exists, but not as elegant). For initialization, use the new operator:

T* my_array;

my_array = new T[3];

or

T* my_array;

my_array = new T[x];

where x is an integer (not necessarily constant as is the case with normal arrays). This way you can take this x from the user at runtime and create your "array" then. Just take care not to forget delete[] my_array after you finish using it to avoid memory leaks.

[Final Note] Using such a dynamically allocated array is a good choice only when you know exactly how many elements you want... either at compile time or even at runtime. So, for example, if after the user supplies his x you'll exactly be using those, that's fine. Otherwise you're facing the danger of overflowing your array (if you need more than x) - which usually crashes the application - or just wasting some space. But even if this is the case, you'll implement most of the functions you need for array manipulation yourself. That's why it's preferable to use containers provided by the C++ Standard Library, like std::vector (as Donotalo mentioned). I just wanted to elaborate on that point more.

Upvotes: 2

K-ballo
K-ballo

Reputation: 81349

The other answers are great, but no one mention using templates to handle this. You should still make your function take a pointer and a size, but templates can fill that automatically for you:

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

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

Upvotes: 10

vz0
vz0

Reputation: 32923

Other languages like Java and Python do store the length of arrays at runtime. In C++ arrays, the length of the array is not stored. That means you need to store it manually somewhere.

Whenever you have a fixed size array on your code, the compiler knows the size of the array since it is reading it from the source code itself. But once the code get compiled, that length information is lost. For example:

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

The compiler won't enforce the size of the array. The following code will silently compile since the array parameter of f1 us just a pointer to the first element of an array:

void h1() {
    double a[10];
    double b[5];

    f1(a); // OK.
    f2(b); // Also OK.
}

Since the compiler ignores the static size of the array when passing it to a function, the only way you have to know the size of an arbitrarily sized array passed as a reference is to explicitly state the size:

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

Then you can call that function with any array:

void h2() {
    double a[10];
    double b[19];

    f2(a, sizeof(a) / sizeof(a[0]));
    f2(b, sizeof(a) / sizeof(a[0]));
}

The parameter array_size contains the actual size of the array.

As a note, sizeof(array) only works on statically defined arrays. When you pass that array to another functions, the size information is lost.

An array is not the same as a pointer. However, an array of undefined size like the parameter of f2 is just a pointer to the first double element in the sequence:

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

For any practicar purpose, f2 and f3 are equivalent.

This is exactly how std::vector works. Internally, a vector is a class with two fields: a pointer to the first element, and the number of elements in the vector. This makes things a little simpler when you want to accept an array of any size as a parameter:

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

Upvotes: 0

Donotalo
Donotalo

Reputation: 13025

In C++, you should use std::vector.

In C/C++, you can't pass arrays as a copy. Arrays are always passed by reference.

EDIT

In C++, arrays passed by reference has different meaning. In both C and C++, arrays decay into a pointer to the first element of the array. Please check the comments below.

Upvotes: 6

Mysticial
Mysticial

Reputation: 471209

A couple things:

  1. C++ doesn't allow variable-sized arrays anyway. So all your arrays will need to have known sizes at compile time. So I'm not entirely sure if your initial question is even applicable since you won't be able to make an array with an unknown size in the first place.

  2. When you pass an array, it is done by reference. It is not copied.

In any case, you'll probably want to consider using vector instead.

EDIT : See comments.

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;
}

Upvotes: 2

cpx
cpx

Reputation: 17557

If I don't pass an array as a ( const double( &numbers ) [length] ), because for example I don't know its size, how do I make sure that it doesn't get copied, but it is referenced?

Yes, it means you're passing an array as reference,

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

Note that the length is a constant integer.

If I pass an array like in the above example, ( double array[] ) is it referenced or is it copied?

No, it is not copied. It means you're passing a pointer to your array which is equivalent to,

void Foo(const double *length);

Upvotes: 3

Related Questions