Passing dynamic arrays

Jonathan M Davis jmdavisProg at gmx.com
Mon Nov 8 17:08:32 PST 2010


On Monday, November 08, 2010 13:54:28 spir wrote:
> On Mon, 08 Nov 2010 15:32:56 -0500
> 
> Jesse Phillips <jessekphillips+D at gmail.com> wrote:
> > But they are past by reference. You can modify the data all you want, but
> > cannot reassign the reference itself.
> 
> No, they are _not_ passed by reference. Stop saying that, this is precisely
> what causes confusion. This is reference semantics:

As Jesse says, they _are_ passed by reference. The struct itself _is_ the 
reference. What makes arrays odd is that anything that resizes it returns a 
_new_ reference or alters the current one. So, using ~= or length can result in 
a reference with a new ptr value and a new length value, and the reference then 
refers to a different block of memory. In the case of an object reference, you 
could implement ~= to return a new reference in exactly the same manner and get 
behavior similar to an array, but no one does that normally.

_Everything_ which is not passed as ref or out is passed by value in D. And 
technically, ref and out probably wrap the value being passed in a pointer and 
that pointer is passed by value. Really, this is easier to understand when 
dealing with pointers than references, since references hide the dereferencing 
process. Take this program for instance:

import std.stdio;

void func1(int* b)
{
    *b = 12;
}

void func2(int* c)
{
    c = new int;
    *c = 7;
}

void func3(int** d)
{
    *d = new int;
    **d = 3;
}


void main()
{
    int* i = new int;
    int* j = i;
    *i = 5;

    writefln("%s %s", *i, *j);

    func1(i);
    writefln("%s %s", *i, *j);

    func2(i);
    writefln("%s %s", *i, *j);

    func3(&i);
    writefln("%s %s", *i, *j);
}


It prints out

5 5
12 12
12 12
3 12

i and j both point to the same memory, so when that memory is altered, the value 
that you get when you dereference them is altered. When func1() is called, the 
value of i is passed to func1() - that is the address that i points to - so b 
points to the same memory that i and j do, so altering the value in that memory, 
alters the memory for all three pointers.

When calling func2(), c is a copy of i and holds the same address. However, 
setting c to a new address means that it no longer points to the same memory and 
any changes made to the memory that it points to does not alter the memory that 
i and j point to. So, they still print out the same value when dereferenced.

func3() takes the address of i. That means that d the holds the address of i and 
can alter i itself rather than just the memory that it points to. c points to 
the memory that holds i itself. So, when assigning the dereferenced c to a new 
address, i itself receives a new address. Then when altering the memory that's 
pointed to by the address that c holds, you alter the memory that i points to 
and change that value. However, while j held the same address as i originally 
and altering the memory at that address therefore altered what both i and j 
pointed to, once i was given a new address by c, they i and j held different 
addresses and altering the memory that one pointed to did not alter the one that 
the other pointed to.

I expect that you've gone over this sort of thing before, but you need to 
realize that references are _exactly_ the same. The only difference is the lack 
of dereferencing step when accessing what it points to. So, whether you're 
passing object reference or an array reference, you're passing that reference by 
value, and any changes to the copy won't change the original. Changes to what 
the reference refers to _will_ change what the original reference points to as 
well since they point to the same thing, but changes to the copied reference 
won't change the original. So, if make the copied reference point to something 
else, then it won't change what the original reference pointed to anymore.

Having an array that you use ~= on is like doing objRef = new Foo(); on a 
reference that was passed into the function. It now points to something else, so 
it's not going to affect the original. What gets somewhat weird about it is that 
~= doesn't necessarily return a new reference, and if it doesn't then the length 
is different, so it still hasn't affected the original reference (if it was going 
to, it would have resulted in a reallocation). The value of the copied reference 
has changed.

I grant you that if you're expecting ~= to return the same reference every time 
and thereby somehow alter the original reference, then you're going to be 
surprised. But it's completely consistent with how pointers and references work. 
You can alter the memory that they point to, but since the pointer or reference 
itself was passed by value, altering it will not alter the original.

- Jonathan M Davis


More information about the Digitalmars-d mailing list