Thread overview
Question about arrays
Apr 21, 2012
Stephen Jones
Apr 21, 2012
Ali Çehreli
Apr 21, 2012
Andrej Mitrovic
Apr 22, 2012
Stephen Jones
Apr 22, 2012
jerro
Apr 22, 2012
jerro
Apr 23, 2012
Ali Çehreli
April 21, 2012
My C programming lies in cobwebs but from memory an array was a pointer to the zeroth index of a set of uniformly sized chunks of memory. I am perplexed to find that in D a call to an array (of float vertices for example) cannot be accomplished by handing &v to functions that need the zeroth index. Is this because D holds a pointer to an array object and the zeroth index is accessed (via compiler background work) to  &v[0]?
April 21, 2012
On 04/21/2012 03:05 PM, Stephen Jones wrote:
> My C programming lies in cobwebs but from memory an array was a pointer
> to the zeroth index of a set of uniformly sized chunks of memory.

Yes and no. :) What you are describing is the feature where an array decays to what you describe.

But there is also the concept of array in C, which is a collection of elements side by side. When you apply sizeof to such a thing, you get the size of the whole thing. So, the whole thing is the array.

But you are right, when passed to functions, they decay to "pointer to first element."

> I am
> perplexed to find that in D a call to an array (of float vertices for
> example) cannot be accomplished by handing &v to functions that need the
> zeroth index. Is this because D holds a pointer to an array object and
> the zeroth index is accessed (via compiler background work) to &v[0]?

In D, arrays are what they should have been in C :). A pointer and a size. Something the equivalent of the following (for the int type):

struct int_Array
{
    int * elements;
    size_t number_of_elements;
}

Correction: That is a slice (aka dynamic array). There is also the fixed-length arrays (aka static arrays) in D.

If you pass the address of a slice, you are passing the address of such a struct variable. So don't bother with the address at all, just pass the array object by value.

Ali

April 21, 2012
On 4/22/12, Stephen Jones <siwenjo@gmail.com> wrote:
> My C programming lies in cobwebs but from memory an array was a pointer to the zeroth index of a set of uniformly sized chunks of memory. I am perplexed to find that in D a call to an array (of float vertices for example) cannot be accomplished by handing &v to functions that need the zeroth index. Is this because D holds a pointer to an array object and the zeroth index is accessed (via compiler background work) to  &v[0]?
>

D array -> struct of a length and pointer to the first element.

If you want to pass arrays to C functions you can do either of these:
float[] arr;
cFunc(arr.ptr, arr.length)
cFunc(&arr[0], arr.length);

I'm assuming the C function needs a length. Maybe this will make things clearer:

void main()
{
    int[] a = new int[](10);
    size_t count = *cast(size_t*)&a;  // first element is the count
    assert(count == 10);

    a[0] = 5;
    a[1] = 100;

    int* iter = &a[0];  // or use a.ptr
    assert(*iter == 5);
    ++iter;
    assert(*iter == 100);
}

Of course you wouldn't really use this style of code in D, but if you have to pass arrays to C use the .ptr field or &arr[0]. On the other hand, passing jagged multidimensional arrays via .ptr to C functions isn't going to work out of the box, but that's another topic (and there's a workaround).

A good article about D arrays/slices is here: http://dlang.org/d-array-article.html
April 22, 2012
Thanks for the replies. I am still uncertain about something. The documentation distinguishes between dynamic slices (int[5] a = new int[5]) which are managed by the runtime, and stack allocated arrays (int[5] b). The problem I have is this. I want to be loading vertex positions and tex-coords of data exported from Blender into openGL vertex buffers. Basically the float data needs to be extracted from a file and passed up to the graphic card's memory but to do so it needs to transit through the CPU memory. Ideally I want to:
    a) load the data from the file into an array built upon the stack and owned by a function
    b) generate the vbo id and send the data off to the graphics card
    c) pass the id back into a class held in managed memory
    d) quit the function thus releasing the array on the stack

My confusion is, if the syntax for both stack based and managed memory arrays is the same (&v[0] or &v.ptr) then both must be objects. I guess the difference is that stack based are of constant length, whereas slices are dynamic because they are managed by the runtime.


April 22, 2012
On Sunday, 22 April 2012 at 22:17:11 UTC, Stephen Jones wrote:
> Thanks for the replies. I am still uncertain about something. The documentation distinguishes between dynamic slices (int[5] a = new int[5]) which are managed by the runtime, and stack allocated arrays (int[5] b). The problem I have is this. I want to be loading vertex positions and tex-coords of data exported from Blender into openGL vertex buffers. Basically the float data needs to be extracted from a file and passed up to the graphic card's memory but to do so it needs to transit through the CPU memory. Ideally I want to:
>     a) load the data from the file into an array built upon the stack and owned by a function
>     b) generate the vbo id and send the data off to the graphics card
>     c) pass the id back into a class held in managed memory
>     d) quit the function thus releasing the array on the stack
>
> My confusion is, if the syntax for both stack based and managed memory arrays is the same (&v[0] or &v.ptr) then both must be objects. I guess the difference is that stack based are of constant length, whereas slices are dynamic because they are managed by the runtime.

Dynamic arrays in D are represented with a struct containing
a pointer to the start of data and a length. Static arrays are
represented in the same way as they are in C - just the data is
saved, the length is known at compile time and the pointer to data
is computed from the stack frame pointer (or a pointer to
struct if the static array is contained within a struct) and the
offset which is also know at compile time, the same way the pointers
to primitive types are computed.  For example the following D program:

import std.stdio;

auto vp(T)(T* a){ return cast(void*) a; }

struct S
{
        int[8] a;
        int[8] b;
        int[]  c;
        int[8] d;
}

void main()
{
        S s;
        writeln( vp(&s.a) - vp(&s.a[0]));
        writeln(vp(&s.b) - vp(&s.a));
        writeln(vp(&s.c) - vp(&s.b));
        writeln(vp(&s.d) - vp(&s.c));
}

and the following C program:

#include <stdio.h>

#define vp(a) (void*)(a)

typedef struct
{
        int* ptr;
        size_t length;
} IntArray;

typedef struct
{
        int a[8];
        int b[8];
        IntArray  c;
        int d[8];
} S;

int main()
{
        S s;
        printf("%d\n", vp(&s.a) - vp(&s.a[0]));
        printf("%d\n", vp(&s.b) - vp(&s.a));
        printf("%d\n", vp(&s.c) - vp(&s.b));
        printf("%d\n", vp(&s.d) - vp(&s.c));
        return 0;
}

both print

0
32
32
16



April 22, 2012
On Sunday, 22 April 2012 at 23:01:26 UTC, jerro wrote:
> On Sunday, 22 April 2012 at 22:17:11 UTC, Stephen Jones wrote:
>> Thanks for the replies. I am still uncertain about something. The documentation distinguishes between dynamic slices (int[5] a = new int[5]) which are managed by the runtime, and stack allocated arrays (int[5] b). The problem I have is this. I want to be loading vertex positions and tex-coords of data exported from Blender into openGL vertex buffers. Basically the float data needs to be extracted from a file and passed up to the graphic card's memory but to do so it needs to transit through the CPU memory. Ideally I want to:
>>    a) load the data from the file into an array built upon the stack and owned by a function
>>    b) generate the vbo id and send the data off to the graphics card
>>    c) pass the id back into a class held in managed memory
>>    d) quit the function thus releasing the array on the stack
>>
>> My confusion is, if the syntax for both stack based and managed memory arrays is the same (&v[0] or &v.ptr) then both must be objects. I guess the difference is that stack based are of constant length, whereas slices are dynamic because they are managed by the runtime.
>
> Dynamic arrays in D are represented with a struct containing
> a pointer to the start of data and a length. Static arrays are
> represented in the same way as they are in C - just the data is
> saved, the length is known at compile time and the pointer to data
> is computed from the stack frame pointer (or a pointer to
> struct if the static array is contained within a struct) and the
> offset which is also know at compile time, the same way the pointers
> to primitive types are computed.  For example the following D program:
>
> import std.stdio;
>
> auto vp(T)(T* a){ return cast(void*) a; }
>
> struct S
> {
>         int[8] a;
>         int[8] b;
>         int[]  c;
>         int[8] d;
> }
>
> void main()
> {
>         S s;
>         writeln( vp(&s.a) - vp(&s.a[0]));
>         writeln(vp(&s.b) - vp(&s.a));
>         writeln(vp(&s.c) - vp(&s.b));
>         writeln(vp(&s.d) - vp(&s.c));
> }
>
> and the following C program:
>
> #include <stdio.h>
>
> #define vp(a) (void*)(a)
>
> typedef struct
> {
>         int* ptr;
>         size_t length;
> } IntArray;
>
> typedef struct
> {
>         int a[8];
>         int b[8];
>         IntArray  c;
>         int d[8];
> } S;
>
> int main()
> {
>         S s;
>         printf("%d\n", vp(&s.a) - vp(&s.a[0]));
>         printf("%d\n", vp(&s.b) - vp(&s.a));
>         printf("%d\n", vp(&s.c) - vp(&s.b));
>         printf("%d\n", vp(&s.d) - vp(&s.c));
>         return 0;
> }
>
> both print
>
> 0
> 32
> 32
> 16

Just to clarify, .ptr and .length aren't really fields, they
are properties. Dynamic arrays are actually represented as
structs containing a pointer and a length, but static arrays
are not. The fact that they have .ptr and .length properties
does not imply they are represented as structs with .ptr and
.length fields.
April 23, 2012
On 04/22/2012 03:17 PM, Stephen Jones wrote:
> Thanks for the replies. I am still uncertain about something. The
> documentation distinguishes between dynamic slices (int[5] a = new
> int[5])

The documentation may be incorrect because int[5] is never a slice. It is a fixed-length array.

As an aside, I don't like calling fixed-length arrays static arrays, because static implies compile time. Fine, the length is known at compile time but as your code above demonstrates, the storage can be allocated at runtime, dynamically.

> which are managed by the runtime, and stack allocated arrays
> (int[5] b).

"Stack allocated" may also be misleading because a fixed-length array can be a part of a type that is allocated dynamically:

class Foo
{
    int[5] b;  // <-- not on the stack
}

As jerro explained, a slice is a pointer to the first element and a length. A fixed-length array is N elements side by side.

> My confusion is, if the syntax for both stack based and managed memory
> arrays is the same (&v[0] or &v.ptr)

You must have meant &v[0] and v.ptr. It is the same for both types:

    // fixed-length
    {
        int[5] a = new int[5];
        auto firstElem_1 = a.ptr;
        auto firstElem_2 = &a[0];
        assert(firstElem_1 == firstElem_2);
    }

    // slice
    {
        int[] a = new int[5];
        auto firstElem_1 = a.ptr;
        auto firstElem_2 = &a[0];
        assert(firstElem_1 == firstElem_2);
    }

Ali

April 23, 2012
On Sat, 21 Apr 2012 18:25:44 -0400, Ali Çehreli <acehreli@yahoo.com> wrote:

> In D, arrays are what they should have been in C :). A pointer and a size. Something the equivalent of the following (for the int type):
>
> struct int_Array
> {
>      int * elements;
>      size_t number_of_elements;
> }

Technically speaking, the number of elements comes first in the memory layout.  The fact that even seasoned D coders get it wrong (in fact, I had to look it up in druntime to be sure!) is a testament to how well-encapsulated D slices actually are.

-Steve