glampert
glampert

Reputation: 4411

Proper way of passing array parameters to D functions

1st Question:

Are D array function parameters always passed by reference, or by value? Also, does the language implements Copy on Write for arrays? E.g.:

void foo(int[] arr)
{
    // is arr a local copy or a ref to an external array?

    arr[0] = 42; // How about now?
}

2nd Question:

Suppose I have a large array that will be passed to function foo as a read-only parameter and it should be avoided as much as possible copying the array, since it is assumed to be a very large object. Which from the following (or none of them) would be the best declaration for function foo:

void foo(const int[] bigArray)

void foo(in int[] bigArray)

void foo(const ref int[] bigArray)

Upvotes: 6

Views: 959

Answers (2)

John Pearcey
John Pearcey

Reputation: 1

From what I've learnt from the docs, static arrays are meant to be passed by value and dynamic arrays by ref. However, this is not actually the case. It depends on the type declaration of the function parameter.

A static array is passed by reference unless you specify the array size in the type of the function parameter. I'm not sure if this is the way it was intended or it's a compiler bug?? It does seem a bit quirky. For one thing, it will allow you to modify the length of the array in the function which should not be allowed for static arrays. If you were to do this using the array pointer, the docs clearly state that the behaviour is undefined.

Here's a quick test I just wrote. Note that the only way to pass by value is to specify int[3] in the function param :

void main{
    int[3] arr_s;
    arr_s[] = 4;
    // arr_s.length = 10; // ERROR: cannot be done for a static array
    
    writeln( "before call ", arr_s );
    function_byValue( arr_s, arr_s.ptr );
    writeln( "after call ", arr_s );

    writeln( "before call ", arr_s );
    function_byRef( arr_s, arr_s.ptr );
    writeln( "after call ", arr_s );
    }

void function_byValue(int[3] arr_s, int* p_s ){

    assert( arr_s.ptr != p_s );
    
    //arr_s.length = 10;
    arr_s[1] = 33;
    writeln( "during call ", arr_s );
    

}

void function_byRef(int[] arr_s, int* p_s ){

    assert( arr_s.ptr == p_s );
    
    //arr_s.length = 10;
    arr_s[1] = 33;
    writeln( "during call ", arr_s );
    

}

Upvotes: 0

Gassa
Gassa

Reputation: 8846

  1. Technically, a dynamic array like int[] is just a pointer and a length. Only the pointer and length get copied onto the stack, not the array contents. An arr[0] = 42; does modify the original array.
    On the other side, a static array like int[30] is a plain old data type consisting of 30 consecutive ints in memory. So, a function like void foo(int[30] arr) would copy 120 bytes onto the stack for a start. In such a case, arr[0] = 42; modifies the local copy of the array.

  2. According to the above, each of the ways you listed avoids copying the array contents. So, whether you need the parameter to be const, in, const ref or otherwise depends on what you are trying to achieve besides avoiding array copy. For example, if you pass a ref int [] arr parameter, not only you can modify its contents, but also you will be able to modify the pointer and length (for example, create a wholly new array and assign it to arr so that it is visible from outside the function).

For further information, please refer to the corresponding articles on the DLang site covering arrays and array slices.

Upvotes: 13

Related Questions