Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

orphaned SO answer

97 views
Skip to first unread message

luser droog

unread,
May 1, 2015, 6:59:28 AM5/1/15
to
I wrote this up but the question was closed before I could
post my answer. So I thought I'd share it here instead. This
is the same material I've shown you all before. The internal
structure of an APL array. But this time I took out all of
the macros so the idea is laid bare, so to speak.

Pursuant to:
http://stackoverflow.com/questions/29981528/how-may-i-create-a-matrix-in-c-using-malloc-and-avoiding-memory-problems

I reply:

I like the other answers here. But I'd like to show a way of defining an array structure that extends to arbitrary dimensions. I learned this from the interpreter for the J language (not the source itself, but lots and lots of "internals" documents) and am using it in my own clone of J.

As several other answers do, we define a struct for the metadata.

typedef struct arr { int k, r, d[1] } *arr;

Now, `d` is a variable-length member which we'll access using C90/99/11 compatible syntax for the *struct hack*. `r` is the *rank* of the array, or number of elements in the dimension list `d`. Now, what is `k`? you might ask.

That's the *evil-genius* part. `k` is `r+2`, which is the number of `int`s in the entire array-header including the dimension list. With this we can dynamically construct a pointer to the data, given a pointer to the header. Now "user" code don't want to be calculating this data pointer by hand, so we put it in a macro.

#define AV(a) (((int*)a)+(a)->k)

Before we can allocate one of these things, we need a helper function to multiply the dimensions.

int tr(int rank, int *dims){
int i,z=1;
for (i=0; i<rank; i++)
z*=dims[i];
return z;
}

arr makearr(int rank, int *dims){
arr z = malloc(sizeof(struct arr) + tr(rank,dims)*sizeof(int));
z->r = rank;
memmove(z->d,dims,rank*sizeof(int));
z->k = (((sizeof(struct arr)+sizeof(int)-1)/sizeof(int))-1) + rank;
return z;
}

Now iterate through one of these, I've found it convenient to iterate through the single index that runs through the whole flattened representation of the array, aka operating *on the ravel*. If the actual indices are needed (to use it the calculation being performed perhaps), they can be generated using the single index and the array of dimensions.

int *vdx(int ind, int *dims, int n, int *vec){
int i,t=ind, *z=vec;
for (i=0; i<n; i++){
z[n-1-i]=t&dims[n-1-i];
t/=dims[n-1-i];
}
return z
}

The inverse function, taking a vector of indices for each dimension and yielding the single index.

int idx(int *vec, int *dims, int n){
int i,z=*vec;
for (i=0; i<n-1; i++){
z*=dims[i+1];
z+=vec[i+1];
}
return z;
}

--
dictated, not read

Ben Bacarisse

unread,
May 1, 2015, 4:59:04 PM5/1/15
to
luser droog <luser...@gmail.com> writes:
<snip>
> Pursuant to:
> http://stackoverflow.com/questions/29981528/how-may-i-create-a-matrix-in-c-using-malloc-and-avoiding-memory-problems

<snip>
> As several other answers do, we define a struct for the metadata.
>
> typedef struct arr { int k, r, d[1] } *arr;
>
> Now, `d` is a variable-length member which we'll access using
> C90/99/11 compatible syntax for the *struct hack*. `r` is the *rank*
> of the array, or number of elements in the dimension list `d`. Now,
> what is `k`? you might ask.
>
> That's the *evil-genius* part. `k` is `r+2`, which is the number of
> `int`s in the entire array-header including the dimension list. With
> this we can dynamically construct a pointer to the data, given a
> pointer to the header. Now "user" code don't want to be calculating
> this data pointer by hand, so we put it in a macro.
>
> #define AV(a) (((int*)a)+(a)->k)

You really should have (((int*)(a))+(a)->k) instead.

Why not ditch k and write (&(a)->d[(a)->r])? If the extra member
avoided evaluating 'a' twice it would be worth it, but what is it
getting you?

> Before we can allocate one of these things, we need a helper function
> to multiply the dimensions.
>
> int tr(int rank, int *dims){
> int i,z=1;
> for (i=0; i<rank; i++)
> z*=dims[i];
> return z;
> }
>
> arr makearr(int rank, int *dims){
> arr z = malloc(sizeof(struct arr) + tr(rank,dims)*sizeof(int));

That size is wrong, but since there are a couple of missing semicolons,
I suspect this is not real code. Maybe the real code has the right size
calculation.

> z->r = rank;
> memmove(z->d,dims,rank*sizeof(int));
> z->k = (((sizeof(struct arr)+sizeof(int)-1)/sizeof(int))-1) + rank;

That looks odd. If there is is odd padding in the struct, I don't see
how it will work, and when there isn't any it's overly complex. I'd
write z->k = &z->d[rank] - (int *)z;

> return z;
> }
>
> Now iterate through one of these, I've found it convenient to iterate
> through the single index that runs through the whole flattened
> representation of the array, aka operating *on the ravel*. If the
> actual indices are needed (to use it the calculation being performed
> perhaps), they can be generated using the single index and the array
> of dimensions.
>
> int *vdx(int ind, int *dims, int n, int *vec){
> int i,t=ind, *z=vec;
> for (i=0; i<n; i++){
> z[n-1-i]=t&dims[n-1-i];
> t/=dims[n-1-i];
> }
> return z
> }

& where % intended, I think, (along with a missing ;). This was made
harder to spot because your apparent aversion to spaces. Why don't you
like them?

<snip>
--
Ben.

luser droog

unread,
May 2, 2015, 1:15:20 AM5/2/15
to
On Friday, May 1, 2015 at 5:59:28 AM UTC-5, luser droog wrote:
> I wrote this up but the question was closed before I could
> post my answer. So I thought I'd share it here instead. This
> is the same material I've shown you all before. The internal
> structure of an APL array. But this time I took out all of
> the macros so the idea is laid bare, so to speak.
>
> Pursuant to:
> http://stackoverflow.com/questions/29981528/how-may-i-create-a-matrix-in-c-using-malloc-and-avoiding-memory-problems
>
> I reply:
>
> I like the other answers here. But I'd like to show a way of defining an array structure that extends to arbitrary dimensions. I learned this from the interpreter for the J language (not the source itself, but lots and lots of "internals" documents) and am using it in my own clone of J.
>
> As several other answers do, we define a struct for the metadata.
>
> typedef struct arr { int k, r, d[1] } *arr;
>
> Now, `d` is a variable-length member which we'll access using C90/99/11 compatible syntax for the *struct hack*. `r` is the *rank* of the array, or number of elements in the dimension list `d`. Now, what is `k`? you might ask.
>
> That's the *evil-genius* part. `k` is `r+2`, which is the number of `int`s in the entire array-header including the dimension list. With this we can dynamically construct a pointer to the data, given a pointer to the header. Now "user" code doesn't want to be calculating this data pointer by hand, so we put it in a macro.
>
> #define AV(a) (((int*)a)+(a)->k)
>
> Before we can allocate one of these things, we need a helper function to multiply the dimensions.
>
> int tr(int rank, int *dims){
> int i,z=1;
> for (i=0; i<rank; i++)
> z*=dims[i];
> return z;
> }
>
> arr makearr(int rank, int *dims){
> arr z = malloc(sizeof(struct arr) + tr(rank,dims)*sizeof(int));
> z->r = rank;
> memmove(z->d,dims,rank*sizeof(int));
> z->k = (((sizeof(struct arr)+sizeof(int)-1)/sizeof(int))-1) + rank;
> return z;
> }
>

C99 compound literals are very useful in calling
the makearr function.

arr m=makearr(2,(int[]){3,3});


> Now to iterate through one of these, I've found it convenient to iterate through the single index that runs through the whole flattened representation of the array, aka operating *on the ravel*. If the actual indices are needed (to use in the calculation being performed perhaps), they can be generated using the single index and the array of dimensions.

/* ind - ravel index
dims - dims[n] (input)
n - number
vec - vec[n] (output) */
> int *vdx(int ind, int *dims, int n, int *vec){
> int i,t=ind, *z=vec;
> for (i=0; i<n; i++){
> z[n-1-i]=t&dims[n-1-i];
> t/=dims[n-1-i];
> }
> return z
> }
>
> The inverse function, taking a vector of indices for each dimension and yielding the single index.
>

/* vec[n] input indices
dims[n] input
n number */
> int idx(int *vec, int *dims, int n){
> int i,z=*vec;
> for (i=0; i<n-1; i++){
> z*=dims[i+1];
> z+=vec[i+1];
> }
> return z;
> }
>


It would probably be better for iterating
to implement a mixed-base increment function
instead of evaluating the whole polynomial
on each iteration.

> --
> dictated, not read

luser droog

unread,
May 2, 2015, 1:38:50 AM5/2/15
to
On Friday, May 1, 2015 at 3:59:04 PM UTC-5, Ben Bacarisse wrote:
> luser droog <luser...@gmail.com> writes:
> <snip>
> > Pursuant to:
> > http://stackoverflow.com/questions/29981528/how-may-i-create-a-matrix-in-c-using-malloc-and-avoiding-memory-problems
>
> <snip>
> > As several other answers do, we define a struct for the metadata.
> >
> > typedef struct arr { int k, r, d[1] } *arr;
> >
> > Now, `d` is a variable-length member which we'll access using
> > C90/99/11 compatible syntax for the *struct hack*. `r` is the *rank*
> > of the array, or number of elements in the dimension list `d`. Now,
> > what is `k`? you might ask.
> >
> > That's the *evil-genius* part. `k` is `r+2`, which is the number of
> > `int`s in the entire array-header including the dimension list. With
> > this we can dynamically construct a pointer to the data, given a
> > pointer to the header. Now "user" code don't want to be calculating
> > this data pointer by hand, so we put it in a macro.
> >
> > #define AV(a) (((int*)a)+(a)->k)
>
> You really should have (((int*)(a))+(a)->k) instead.

Oops. Ack.

> Why not ditch k and write (&(a)->d[(a)->r])? If the extra member
> avoided evaluating 'a' twice it would be worth it, but what is it
> getting you?

It isn't shown here, but it allows me to create an "indirect" array
by setting .k to the ptrdiff_t between the actual data and the
array header. This lets me cheaply create slices of an array and
treat each one as an array in its own right. I suppose the same
thing could be achieved with less obfuscation by making .k a pointer
instead of an offset.

> > Before we can allocate one of these things, we need a helper function
> > to multiply the dimensions.
> >
> > int tr(int rank, int *dims){
> > int i,z=1;
> > for (i=0; i<rank; i++)
> > z*=dims[i];
> > return z;
> > }
> >
> > arr makearr(int rank, int *dims){
> > arr z = malloc(sizeof(struct arr) + tr(rank,dims)*sizeof(int));
>
> That size is wrong, but since there are a couple of missing semicolons,
> I suspect this is not real code. Maybe the real code has the right size
> calculation.

It's a little larger than necessary since it does not subtract-off the
size. Oh, I see now. It needs to add rank*sizeof(int) for the dimensions.
Yes, this code was written while looking at the real code, but
translating the heavy usage of macros to more straightforward C.

> > z->r = rank;
> > memmove(z->d,dims,rank*sizeof(int));
> > z->k = (((sizeof(struct arr)+sizeof(int)-1)/sizeof(int))-1) + rank;
>
> That looks odd. If there is is odd padding in the struct, I don't see
> how it will work, and when there isn't any it's overly complex. I'd
> write z->k = &z->d[rank] - (int *)z;

Nice. That is much simpler.

> > return z;
> > }
> >
> > Now iterate through one of these, I've found it convenient to iterate
> > through the single index that runs through the whole flattened
> > representation of the array, aka operating *on the ravel*. If the
> > actual indices are needed (to use it the calculation being performed
> > perhaps), they can be generated using the single index and the array
> > of dimensions.
> >
> > int *vdx(int ind, int *dims, int n, int *vec){
> > int i,t=ind, *z=vec;
> > for (i=0; i<n; i++){
> > z[n-1-i]=t&dims[n-1-i];
> > t/=dims[n-1-i];
> > }
> > return z
> > }
>
> & where % intended, I think, (along with a missing ;). This was made
> harder to spot because your apparent aversion to spaces. Why don't you
> like them?

Probably too much time spent on codegolf.stackexchange.com.

--
ths s yr brn n cdglf

asetof...@gmail.com

unread,
May 2, 2015, 4:48:59 AM5/2/15
to
I not understand good, excuse etc
If i have
Matrix 1 would be Array
Matrix 2 would be a matrix
of index (i,j)
Matrix 3 would be a matrix
of index (i,j,k)
Etc
Matrix n would be a matrix
of index (i1, i2, ... in-1)
What n i have to consider?

A generic n?
A generic element size?
Thanks

Ben Bacarisse

unread,
May 2, 2015, 6:08:42 AM5/2/15
to
luser droog <luser...@gmail.com> writes:

> On Friday, May 1, 2015 at 3:59:04 PM UTC-5, Ben Bacarisse wrote:
>> luser droog <luser...@gmail.com> writes:
>> <snip>
>> > Pursuant to:
>> > http://stackoverflow.com/questions/29981528/how-may-i-create-a-matrix-in-c-using-malloc-and-avoiding-memory-problems
>>
>> <snip>
>> > As several other answers do, we define a struct for the metadata.
>> >
>> > typedef struct arr { int k, r, d[1] } *arr;
>> >
>> > Now, `d` is a variable-length member which we'll access using
>> > C90/99/11 compatible syntax for the *struct hack*. `r` is the *rank*
>> > of the array, or number of elements in the dimension list `d`. Now,
>> > what is `k`? you might ask.
>> >
>> > That's the *evil-genius* part. `k` is `r+2`, which is the number of
>> > `int`s in the entire array-header including the dimension list. With
>> > this we can dynamically construct a pointer to the data, given a
>> > pointer to the header. Now "user" code don't want to be calculating
>> > this data pointer by hand, so we put it in a macro.
>> >
>> > #define AV(a) (((int*)a)+(a)->k)
<snip>
>> Why not ditch k and write (&(a)->d[(a)->r])? If the extra member
>> avoided evaluating 'a' twice it would be worth it, but what is it
>> getting you?
>
> It isn't shown here, but it allows me to create an "indirect" array
> by setting .k to the ptrdiff_t between the actual data and the
> array header. This lets me cheaply create slices of an array and
> treat each one as an array in its own right. I suppose the same
> thing could be achieved with less obfuscation by making .k a pointer
> instead of an offset.

So your reason for having k is *not* the one you gave in the message I
replied to? Oh well... Can you give an example where it really *does*
help?

<snip>
--
Ben.

luser droog

unread,
May 2, 2015, 3:19:52 PM5/2/15
to
I think you have the basic idea, but yes,
it quickly becomes very confusing when you
try to grok the whole gestalt.

To simplify things, consider the element size
to be constant (you can remove this restriction
later on). So it's all `int`s. All the same size.

So basically what we're doing is two applications
of the struct hack; or two levels of Pascal-string-
style data layout.

1 R Product(D)
--- -------------------- -----------------------------
R D[0] D[1] ... D[R-1] V[0] V[1] ... V[Product(D)-1]

Since the starting position of V is variable, I use a
macro to calculate the pointer V.

Then you can define functions to operate on the data.

As you may have surmised, the data structure accommodates
arbitrary dimensions, but a function will usually be defined
for arguments of certain fixed dimensions.

In APL functions are extended to higher dimensions by
treating extra dimensions as the "frame" of the data,
and recursively applying the function to "cells" which
are the fixed size that function is defined to handle.

luser droog

unread,
May 2, 2015, 3:40:20 PM5/2/15
to
I do, but it's straight from the interpreter and uses macros to access
all the members of struct arr; There's also an .n member which caches
the product-of-dimensions calc. `struct arr` is actually called
`struct a` and `typedef ... *arr` is actually `typedef ... *A`.

These are part of a dozen or so macros which handle extending a
function to higher dimensions. LFRAME creates an array called lf
which uses k to point to a portion of the dimension vector of
the function's left argument (always called `a`).

It creates this indirect array by punning the data structure.
It statically allocates an array header which serendipitously
(thanks to the definition of d[1] above) has room for one
dimension value. Then it changes the rank to one, sets
dim[0] to the length we want to access, and sets k to
the crazy ptrdiff_t. ... So basically, it lets me *cast*
my array structure upon data that's already contiguous in
memory.

/* fill-in array f with rk elements of ar's shape vector */
#define LOADFRAME(f,ar,rk) \
/*P("rk=%d\n",rk);*/ \
if (AR(ar)-(rk)>=0) { \
/*f = ga(0,AR(ar)?AR(ar)==1?0:1:0,(I[]){AR(ar)-(rk)?AR(ar)-(rk):1});*/ \
/*mv(AV(f),AR(ar)-(rk)?AD(ar):(I[]){0},AR(ar)-(rk)?AR(ar)-(rk):1);*/ \
AR(f)=AR(ar)-(rk)>0?1:0; \
AN(f)=AD(f)[0]=AR(ar)-(rk); \
AK(f)=((C*)AD(ar))-((C*)f); /*make "indirect" array of ar's frame shape*/ \
} \
/*P("AR(f)=%d\n", AR(f));*/

/* load the left frame */
#define LFRAME(rk) \
/*A lf = 0;*/ \
A lf = &(struct a){.t=INT,.n=0,.r=0}; \
if ((rk)<0) { LOADFRAME(lf,a,AR(a)+(rk)) } \
else { LOADFRAME(lf,a,rk) }

Ben Bacarisse

unread,
May 2, 2015, 6:32:47 PM5/2/15
to
Where? It's not in the post, is it?

> but it's straight from the interpreter and uses macros to access
> all the members of struct arr; There's also an .n member which caches
> the product-of-dimensions calc. `struct arr` is actually called
> `struct a` and `typedef ... *arr` is actually `typedef ... *A`.

Is the name significant? Have you posted something I've missed?

> These are part of a dozen or so macros which handle extending a
> function to higher dimensions. LFRAME creates an array called lf
> which uses k to point to a portion of the dimension vector of
> the function's left argument (always called `a`).

But where is the example that shows how k helps?

> It creates this indirect array by punning the data structure.
> It statically allocates an array header which serendipitously
> (thanks to the definition of d[1] above) has room for one
> dimension value. Then it changes the rank to one, sets
> dim[0] to the length we want to access, and sets k to
> the crazy ptrdiff_t. ... So basically, it lets me *cast*
> my array structure upon data that's already contiguous in
> memory.

Where is the code? Without it, you can assert that k is useful and
everyone will just have to nod and say OK. Surely that was not the
point of posting was it?

> /* fill-in array f with rk elements of ar's shape vector */
> #define LOADFRAME(f,ar,rk) \
> /*P("rk=%d\n",rk);*/ \
> if (AR(ar)-(rk)>=0) { \
> /*f = ga(0,AR(ar)?AR(ar)==1?0:1:0,(I[]){AR(ar)-(rk)?AR(ar)-(rk):1});*/ \
> /*mv(AV(f),AR(ar)-(rk)?AD(ar):(I[]){0},AR(ar)-(rk)?AR(ar)-(rk):1);*/ \
> AR(f)=AR(ar)-(rk)>0?1:0; \
> AN(f)=AD(f)[0]=AR(ar)-(rk); \
> AK(f)=((C*)AD(ar))-((C*)f); /*make "indirect" array of ar's frame shape*/ \
> } \
> /*P("AR(f)=%d\n", AR(f));*/
>
> /* load the left frame */
> #define LFRAME(rk) \
> /*A lf = 0;*/ \
> A lf = &(struct a){.t=INT,.n=0,.r=0}; \
> if ((rk)<0) { LOADFRAME(lf,a,AR(a)+(rk)) } \
> else { LOADFRAME(lf,a,rk) }

How does this help? It's full of undefined macros and makes no
reference to a member called "k".

--
Ben.

James Kuyper

unread,
May 2, 2015, 9:20:00 PM5/2/15
to
On 05/02/2015 01:38 AM, luser droog wrote:
> On Friday, May 1, 2015 at 3:59:04 PM UTC-5, Ben Bacarisse wrote:
...
>> I suspect this is not real code.
...
> Yes, this code was written while looking at the real code, but
> translating the heavy usage of macros to more straightforward C.

It's probably best to do that automatically, rather than translating by
hand. Most C compilers I've known have had the option of producing the
out put of the pre-processing phase. You could have then cleaned it up
before posting (I'd recommend a test run to confirm that the "clean-up"
didn't change the actual behavior of the code).


--
James Kuyper

luser droog

unread,
May 3, 2015, 12:44:20 AM5/3/15
to
> > I do [have an example],
>
> Where? It's not in the post, is it?

It is the code in the same post. I didn't give enough info, it seems.
This the real definition that it operates on and the macros that it
uses. The .k member is actually used as a byte-offset rather than an
int-offset as described upthread.

typedef struct a{I t, r, n, k, d[1];}*A; /* The abstract array header */
#define AT(a) ((a)->t) /* Type (takes values from enum type) */
#define AR(a) ((a)->r) /* Rank (size of Dims) */
#define AN(a) ((a)->n) /* Number of values in ravel */
#define AK(a) ((a)->k) /* Offset of ravel */
#define AD(a) ((a)->d) /* Dims */
#define AV(a) ((I*)(((C*)a)+AK(a))) /* Values in ravelled order */
enum type { INT, BOX, SYMB, CHAR, NUM, DBL, MRK, NLL, VRB, NTYPES };


> > but it's straight from the interpreter and uses macros to access
> > all the members of struct arr; There's also an .n member which caches
> > the product-of-dimensions calc. `struct arr` is actually called
> > `struct a` and `typedef ... *arr` is actually `typedef ... *A`.
>
> Is the name significant? Have you posted something I've missed?

I thought that would be enough to understand the snippet. Each of the
macros accesses the array struct member with the lowercase version
of the second character, ie. AK -> a->k, AD(a) ((a)->d), etc.


> > These are part of a dozen or so macros which handle extending a
> > function to higher dimensions. LFRAME creates an array called lf
> > which uses k to point to a portion of the dimension vector of
> > the function's left argument (always called `a`).
>
> But where is the example that shows how k helps?

It's in the code snippet. The two commented-out lines
are one way of constructing this frame-vector that was
needed. ga() is like the makearr() function shown, but
the first argument is the type code.


> > It creates this indirect array by punning the data structure.
> > It statically allocates an array header which serendipitously
> > (thanks to the definition of d[1] above) has room for one
> > dimension value. Then it changes the rank to one, sets
> > dim[0] to the length we want to access, and sets k to
> > the crazy ptrdiff_t. ... So basically, it lets me *cast*
> > my array structure upon data that's already contiguous in
> > memory.
>
> Where is the code? Without it, you can assert that k is useful and
> everyone will just have to nod and say OK. Surely that was not the
> point of posting was it?

This is the code that uses it. Perhaps I should backpedal from "useful",
defined as "full of uses", because my investigations are ongoing.
It does have this one use which I consider significant from an
implementation-efficiency point of view, which is that I can replace
code which allocates a temporary array and performs a copy (the mv()
function, which is basically memmove scaled by sizeof(int)), with
this manipulation of the .k member as a ptrdiff_t to a different
array. I don't have garbage collection implemented yet, but this will
mean one less temporary object to keep track of, and less burden on
the memory allocation system, and less (unnecessary) copying of data.

> > /* fill-in array f with rk elements of ar's shape vector */
> > #define LOADFRAME(f,ar,rk) \
> > /*P("rk=%d\n",rk);*/ \
> > if (AR(ar)-(rk)>=0) { \
> > /*f = ga(0,AR(ar)?AR(ar)==1?0:1:0,(I[]){AR(ar)-(rk)?AR(ar)-(rk):1});*/ \
> > /*mv(AV(f),AR(ar)-(rk)?AD(ar):(I[]){0},AR(ar)-(rk)?AR(ar)-(rk):1);*/ \
> > AR(f)=AR(ar)-(rk)>0?1:0; \
> > AN(f)=AD(f)[0]=AR(ar)-(rk); \
> > AK(f)=((C*)AD(ar))-((C*)f); /*make "indirect" array of ar's frame shape*/ \
> > } \
> > /*P("AR(f)=%d\n", AR(f));*/
> >
> > /* load the left frame */
> > #define LFRAME(rk) \
> > /*A lf = 0;*/ \
> > A lf = &(struct a){.t=INT,.n=0,.r=0}; \
> > if ((rk)<0) { LOADFRAME(lf,a,AR(a)+(rk)) } \
> > else { LOADFRAME(lf,a,rk) }
>
> How does this help? It's full of undefined macros and makes no
> reference to a member called "k".

The AK() macro accesses the ->k member of the array header.

All of the real code is from
https://github.com/luser-dr00g/inca/blob/master/inca3.c

A more fully-realized (but less fully-featured) implementation
is at
https://github.com/luser-dr00g/inca/blob/master/inca2.c

Ben Bacarisse

unread,
May 3, 2015, 6:23:08 AM5/3/15
to
The example you posted did not show the value in having this extra data
member. And I've still not seen any code that show its value.

> This the real definition that it operates on and the macros that it
> uses. The .k member is actually used as a byte-offset rather than an
> int-offset as described upthread.
>
> typedef struct a{I t, r, n, k, d[1];}*A; /* The abstract array header */
> #define AT(a) ((a)->t) /* Type (takes values from enum type) */
> #define AR(a) ((a)->r) /* Rank (size of Dims) */
> #define AN(a) ((a)->n) /* Number of values in ravel */
> #define AK(a) ((a)->k) /* Offset of ravel */
> #define AD(a) ((a)->d) /* Dims */
> #define AV(a) ((I*)(((C*)a)+AK(a))) /* Values in ravelled order */
> enum type { INT, BOX, SYMB, CHAR, NUM, DBL, MRK, NLL, VRB, NTYPES };

(missing ()s round a there)


>
>
>> > but it's straight from the interpreter and uses macros to access
>> > all the members of struct arr; There's also an .n member which caches
>> > the product-of-dimensions calc. `struct arr` is actually called
>> > `struct a` and `typedef ... *arr` is actually `typedef ... *A`.
>>
>> Is the name significant? Have you posted something I've missed?
>
> I thought that would be enough to understand the snippet.

You thought I could (or worse, should) guess what these macros do?

> Each of the
> macros accesses the array struct member with the lowercase version
> of the second character, ie. AK -> a->k, AD(a) ((a)->d), etc.

Except for the only one you posted in the original, AV. Thus you could
not possible expect me to guess that the others were just member
accesses.

>> > These are part of a dozen or so macros which handle extending a
>> > function to higher dimensions. LFRAME creates an array called lf
>> > which uses k to point to a portion of the dimension vector of
>> > the function's left argument (always called `a`).
>>
>> But where is the example that shows how k helps?
>
> It's in the code snippet. The two commented-out lines
> are one way of constructing this frame-vector that was
> needed. ga() is like the makearr() function shown, but
> the first argument is the type code.

The crucial code is comment out??? The reason to have the extra data
member is revealed when I read code that is not needed? Are you being
serious?
But, at the risk of sounding obsessive, where is the value in having
this extra data member? You've shown code that sets it, and commented
out code that uses it -- but in a macro that I previsouly suggested
should be written *without* using k. I still have no idea what value
you think it has.

<snip>
--
Ben.

asetof...@gmail.com

unread,
May 3, 2015, 12:33:11 PM5/3/15
to
This is my try for a i0xi1x...xin Matrix

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define G(a,b) if(a)goto b
#define R return
#define P printf
#define M malloc
#define F free
#define S sizeof
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned
#define i32 int

typedef struct {u32 sz; u32 *pInd; u8 *data;}Mtrx;
/*
pInd[0] is the number of index as 32 bit
pInd[1] is the first max index as 32 bit
....
pInd[pInd[0]] is the last max index
pInd[pInd[0]+1] is the size of element as 32 bit

data is a pointer to matrix

se pInd[0]==1 => pInd[1]=Index0 pInd[2]=size 0..2 3
se pInd[0]==2 => pInd[1]=Index0 pInd[2]=Index1 pInd[3]=size 0..3 4

*/


/*
who reserve mem space to matrix of element msize
for dimension
i0xi1x...xin taken as input
where i0, i1 etc are max index and n<=255
the last max index is -1 that end the input
Return -1 for error
Return 0 all ok
NB
**The first function to call on pm [construct matrix] **
**the last function to call on pm [destruct matrix] **
*/
i32 Mcon(Mtrx* pm, u32 msize, ...)
{va_list ap;
u32 v, i, s, j;
u32 arr[256];

G(pm, l1);
l0: R -1; // for error
n0: va_end(ap); pm->pInd=0; pm->data=0; pm->sz=0; G(1,l0);
n1: F(pm->pInd); G(1,n0);
l1: G(msize, l3); /* free the Matrix */
F(pm->pInd); F(pm->data);
pm->pInd=0; pm->data=0; pm->sz=0;
l2: R 0; // for all ok
y0: va_end(ap); G(1,l2);
l3: va_start(ap, msize); G(msize>0xFFF, n0); i=0;
l4: v=va_arg(ap, u32); //v the first index;if i==0 means no element
G(v>0xFFFF && v!= -1, n0); G(v!=-1, la); G(i==0, n0);
pm->pInd=M( (i+2)* S(u32) ); G(pm->pInd==0, n0);
//0[nIndex] 1[index0]..n[index n-1] n+1[size] [n+2 vars]
s=1; j=1; pm->pInd[0]=i;
l5: pm->pInd[j]=arr[j-1]; s*=arr[j-1]; G(s>0xFFFFF, n1);
l6: ++j; G( j<=i, l5);
pm->pInd[j]=msize; s*=msize; G(s>0xFFFFF, n1);
pm->data=M(s); G(pm->data==0, n1); pm->sz=s; G(1,y0);
la: G(i>255, n0); arr[i++]=v; G(1,l4);
}

// return one pointer to element
// or 0 if find some error
u8* Aelm(Mtrx* m, ...)
{va_list ap;
u8 *r;
u32 i,j,maxn, max, size, v, s, Index[256];

G(m,l1);
l0: R 0;
n0: va_end(ap); G(1,l0);
l1: maxn=m->pInd[0]; size=m->pInd[maxn+1]; // 0 1 maxn 134
i=1; G(maxn>=256, l0); // for 2x2 y*n+x (y,x)e[0..m-1]x[0..n-1]
va_start(ap, m); // gets all index i0 i1 maxn=2
l2: v=va_arg(ap, u32); // gets one index
G(v>= m->pInd[i], n0); // if that index > max index error
Index[i-1]=v; // save that index
++i; G(i<=maxn, l2);
v=va_arg(ap, u32); G(v!= -1, n0); // ceck value
i=0; s=Index[maxn-1]; // s is the char index where is the elment
l3: s=s+Index[i]*(m->pInd[i+2]); ++i; G(i<maxn-1, l3);
v=s*size; r=m->data; r+=v;
va_end(ap);
R r;
}

// for now only nxm matrix of u8 u16 u32
void PrntMtrx(Mtrx* m)
{va_list ap;
u8 *p, *pmax;
u32 i,j,maxn, max, size, v;
u32 Index[256];

G(m, l1);
l0: R; // error
l1: G(m->pInd[0]>2, l0);
size=m->pInd[3];
G(size>=8, l0);
i=0; p=m->data; pmax=p+(size*m->pInd[1]*m->pInd[2]);
m0: G(size==1, l2); G(size==2, l3); G(size==4,l4); G(1,l0);
l2: v=*(u8*) p; G(1, l5);
l3: v=*(u16*)p; G(1, l5);
l4: v=*(u32*)p;
l5: P("%u ", v);
G(((i+1)%(m->pInd[2]))!=0, l6); P("\n");
l6: p+=size; ++i; G(p<pmax, m0);
R;
}


int main(void)
{Mtrx m1, m2, m3, m4;
int j=S(u32);

G(Mcon(&m1, S(u32), 2, 2, -1), z); // constructor m1 Matrix 2x2 of u32 element
G(Mcon(&m2, S(u32), 2, 3, -1), z);
G(Mcon(&m3, S(u32), 2, 1, -1), z);
G(Mcon(&m4, S(u32), 1, 2, -1), z);

// fill the matrix
*(u32*)Aelm(&m1, 0,0, -1)=123; *(u32*)Aelm(&m1, 0,1, -1)=124;
*(u32*)Aelm(&m1, 1,0, -1)=125; *(u32*)Aelm(&m1, 1,1, -1)=126;

*(u32*)Aelm(&m2, 0,0, -1)=127; *(u32*)Aelm(&m2, 0,1, -1)=128; *(u32*)Aelm(&m2, 0,2, -1)=129;
*(u32*)Aelm(&m2, 1,0, -1)=130; *(u32*)Aelm(&m2, 1,1, -1)=131; *(u32*)Aelm(&m2, 1,2, -1)=132;

*(u32*)Aelm(&m3, 0,0, -1)=133;
*(u32*)Aelm(&m3, 1,0, -1)=134;

*(u32*)Aelm(&m4, 0,0, -1)=135; *(u32*)Aelm(&m4, 0,1, -1)=136;

//print the matrix
P("M1:\n");PrntMtrx(&m1); P("\n\n"); P("M2:\n"); PrntMtrx(&m2); P("\n\n");
P("M3:\n");PrntMtrx(&m3); P("\n\n"); P("M4:\n"); PrntMtrx(&m4); P("\n\n");

z: G(Mcon(&m4, 0, 0, 0, 0), z);
G(Mcon(&m3, 0, 0, 0, 0), z);
G(Mcon(&m2, 0, 0, 0, 0), z); // destructor m2
G(Mcon(&m1, 0, 0, 0, 0), z);

R 0;
}
------------
M1:
123 124
125 126


M2:
127 128 129
130 131 132


M3:
133
134


M4:
135 136



luser droog

unread,
May 4, 2015, 1:35:21 AM5/4/15
to
On Friday, May 1, 2015 at 3:59:04 PM UTC-5, Ben Bacarisse wrote:
> luser droog <luser...@gmail.com> writes:
> <snip>
> > Pursuant to:
> > http://stackoverflow.com/questions/29981528/how-may-i-create-a-matrix-in-c-using-malloc-and-avoiding-memory-problems
>
> <snip>
> > As several other answers do, we define a struct for the metadata.
> >
> > typedef struct arr { int k, r, d[1] } *arr;
> >
> > Now, `d` is a variable-length member which we'll access using
> > C90/99/11 compatible syntax for the *struct hack*. `r` is the *rank*
> > of the array, or number of elements in the dimension list `d`. Now,
> > what is `k`? you might ask.
> >
> > That's the *evil-genius* part. `k` is `r+2`, which is the number of
> > `int`s in the entire array-header including the dimension list. With
> > this we can dynamically construct a pointer to the data, given a
> > pointer to the header. Now "user" code don't want to be calculating
> > this data pointer by hand, so we put it in a macro.
> >
> > #define AV(a) (((int*)a)+(a)->k)
>
> You really should have (((int*)(a))+(a)->k) instead.
>
> Why not ditch k and write (&(a)->d[(a)->r])? If the extra member
> avoided evaluating 'a' twice it would be worth it, but what is it
> getting you?

After much time, I think I begin to understand what you're saying.
And you're right. We don't need k at this point in the presentation.
It allows certain optimizations like sharing values and cheap row
slices by supplying a hook for internal indirection, but it is not
necessary. In fact my first implementation did not use it (it also
had a maximum of 3 dimensions).

It gives me extra indirection for sharing data and avoiding
unnecessary allocations, but I am unable to produce a simple
example that illustrates this. There are several uses in my
implementation so far, but they are several levels deep in macros
and I'm reluctant to try to make them presentable (very recent
big failures in that area).

I had never considered using r directly like you show here. I'll
have to ponder its merits at greater length.


... In related news, I'm reworking the write-up as a Share-your-
Knowledge Q-and-A pair for SO. I think I can do without k as Ben
suggests, unless possibly in an *optimizations/extensions* appendix
at the end.

--
droog
Very appreciative at the irritating responses from Ben

luser droog

unread,
May 4, 2015, 2:34:56 AM5/4/15
to
On Friday, May 1, 2015 at 5:59:28 AM UTC-5, luser droog wrote:
> I wrote this up but the question was closed before I could
> post my answer. So I thought I'd share it here instead. This
> is the same material I've shown you all before. The internal
> structure of an APL array. But this time I took out all of
> the macros so the idea is laid bare, so to speak.
>
> Pursuant to:
> http://stackoverflow.com/questions/29981528/how-may-i-create-a-matrix-in-c-using-malloc-and-avoiding-memory-problems
>

A revised, simplified, and TESTED write-up of this material has
been posted to:
http://stackoverflow.com/questions/30023867/how-can-i-work-with-dynamically-allocated-arbitrary-dimensional-arrays/30023868#30023868

asetof...@gmail.com

unread,
May 4, 2015, 5:30:34 AM5/4/15
to
this only use 1 malloc call for allocate Matrix
Are other constrains ?

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define G(a,b) if(a)goto b
#define R return
#define P printf
#define M malloc
#define F free
#define S sizeof
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned
#define i32 int
#define d64 double


/*
Each element until sz is u32
pInd[0],pInd[1],..,pInd[pInd[0]], sizeElement, sz, [matrix data]
0 1 pInd[0] pInd[0]+1 pInd[0]+2 indirizzo
1 2 pInd[0]+1 pInd[0]+2 pInd[0]+3 nelement
pInd[0]: is the number of index as 32 bit
pInd[1]: is the first max index as 32 bit
....
pInd[pInd[0]]: is the last max index
pInd[pInd[0]+1]: is the size of element as 32 bit

sz: size of "data" area
data is matrix "data" area
*/

// access to size of matrix data as pointer to u32
#define SZ(a) ((a)+*(a)+2)
// access to size element of matrix
#define SZe(a) ((a)+*(a)+1)
// access to data as pointer to u8
#define Data(a) (unsigned char*) ((a)+*(a)+3)

/*
reserve mem space to matrix of element msize for dimension
i0xi1x...xin taken as input
where i0, i1 etc are max+1 index and n<=255
the last max+1 index is -1 that end the input
Return -1 for error
Return 0 all ok
NB
**The first function to call on pm [construct matrix] **
**the last function to call on pm [destruct matrix] **
*/
i32 Mcon(u32** pm, u32 msize, ...)
{va_list ap;
u32 v, i, s, j;
u32 arr[256];

G(pm, l1);
l0: R -1; // for error
n0: va_end(ap); *pm=0; G(1,l0);
l1: G(msize, l3); F(*pm); /* free the Matrix */
l2: R 0; // for all ok
y0: va_end(ap); G(1,l2);
l3: va_start(ap, msize); G(msize>0xFFF, n0); G(msize==1, n0); i=0;
l4: v=va_arg(ap, u32); //v the first index;if i==0 means no element
G(v>0xFFFF && v!= -1, n0); G(v!=-1, la); G(i==0, n0);
s=1; j=1;
l5: s*=arr[j-1]; G(s>0xFFFFF, n0); ++j; G( j<=i, l5);
s*=msize; G(s>0xFFFFF, n0);
*pm=M((i+3)*sizeof(u32)+s); G(*pm==0, n0);
(*pm)[0]=i; *SZe(*pm)=msize; *SZ(*pm)=s; j=1;
l6: (*pm)[j]=arr[j-1]; ++j; G( j<=i, l6);
G(1,y0);
la: G(i>255, n0); arr[i++]=v; G(1,l4);
}

// return one pointer to element
// or 0 if find some error
u8* Aelm(u32* pm, ...)
{va_list ap;
u8 *r;
u32 i,j,maxn, max, size, v, s, Index[256];

G(pm,l1);
l0: R 0;
n0: va_end(ap); G(1,l0);
l1: maxn=pm[0]; size= *SZe(pm); // 0 1
i=1; G(maxn>=256||maxn==0, l0); //for 2x2 y*n+x (y,x)e[0..m-1]x[0..n-1]
va_start(ap, pm); // gets all index i0 i1 maxn=2
l2: v=va_arg(ap, u32); // gets one index
G(v>=pm[i], n0); // if that index >= sup index error
Index[i-1]=v; // save that index
++i; G(i<=maxn, l2);
v=va_arg(ap, u32); G(v!= -1, n0); // ceck value
i=0; s=Index[maxn-1]; // s is the index where is the elment
l3: s=s+Index[i]*(pm[i+2]); ++i; G(i<maxn-1, l3);
v=s*size; r=Data(pm); r+=v;
va_end(ap);
R r;
}

// for now only nxm matrix of u8 u16 u32 d64[double]
void PrntMtrx(u32* m)
{va_list ap;
d64 dv;
u8 *p, *pmax;
u32 i,j,maxn, max, size, v;
u32 Index[256];

G(m, l1);
l0: R; // error
l1: G(m[0]>2, l0);
size= *SZe(m);
G(size>8, l0);
i=0; p=Data(m); pmax=p+(size*m[1]*m[2]);
m0: G(size==1,l2);G(size==2,l3);G(size==4,l4);G(size==8,l11);G(1,l0);
l11:dv=*(d64*)p; P("%10.2f",dv); G(1,l5);
l2: v=*(u8*) p; P("%3u ", v); G(1,l5);
l3: v=*(u16*)p; P("%5u ", v); G(1,l5);
l4: v=*(u32*)p; P("%10u ", v);
l5: G( (i+1)% m[2] != 0, l6); P("\n");
l6: p+=size; ++i; G(p<pmax, m0);
R;
}


int main(void)
{u32 *m1=0, *m2=0, *m3=0, *m4=0, *m5=0;

G(Mcon(&m1, S(u32), 2, 2, -1), z); // constructor m1 Matrix 2x2 of u32 element
G(Mcon(&m2, S(u32), 2, 3, -1), z);
G(Mcon(&m3, S(u32), 2, 1, -1), z);
G(Mcon(&m4, S(u32), 1, 2, -1), z);
G(Mcon(&m5, S(d64), 2, 2, -1), z);

// fill the matrix
*(u32*)Aelm(m1, 0,0, -1)=123; *(u32*)Aelm(m1, 0,1, -1)=124;
*(u32*)Aelm(m1, 1,0, -1)=125; *(u32*)Aelm(m1, 1,1, -1)=126;

*(u32*)Aelm(m2, 0,0, -1)=127; *(u32*)Aelm(m2, 0,1, -1)=128; *(u32*)Aelm(m2, 0,2, -1)=129;
*(u32*)Aelm(m2, 1,0, -1)=130; *(u32*)Aelm(m2, 1,1, -1)=131; *(u32*)Aelm(m2, 1,2, -1)=132;

*(u32*)Aelm(m3, 0,0, -1)=133;
*(u32*)Aelm(m3, 1,0, -1)=134;

*(u32*)Aelm(m4, 0,0, -1)=135; *(u32*)Aelm(m4, 0,1, -1)=136;

*(d64*)Aelm(m5, 0,0, -1)=123456.123; *(d64*)Aelm(m5, 0,1, -1)=12345.124;
*(d64*)Aelm(m5, 1,0, -1)=123.744; *(d64*)Aelm(m5, 1,1, -1)=1.749;

//print the matrix
P("M1:\n");PrntMtrx(m1); P("\n\n"); P("M2:\n"); PrntMtrx(m2); P("\n\n");
P("M3:\n");PrntMtrx(m3); P("\n\n"); P("M4:\n"); PrntMtrx(m4); P("\n\n");

P("M5:\n"); PrntMtrx(m5); P("\n\n");

z: Mcon(&m5, 0); // destructor
Mcon(&m4, 0);
Mcon(&m3, 0);
Mcon(&m2, 0);
Mcon(&m1, 0);
R 0;
}

-------------------------------
M1:
123 124
125 126


M2:
127 128 129
130 131 132


M3:
133
134


M4:
135 136


M5:
123456.12 12345.12
123.74 1.75

asetof...@gmail.com

unread,
May 4, 2015, 4:44:27 PM5/4/15
to
In all your code is absent error
check... And this is danger in C
for example check malloc not fail;
variable not overflow; args
are in the right range etc

But if
a[n][m]=7;
But if n>nmax this would write
out of memory?
I think could be easy write a not
in range index etc

asetof...@gmail.com

unread,
May 8, 2015, 12:14:07 PM5/8/15
to
In a[n][m] if one index are > max
index, what would be the
right operation
write out the array space (as C) or
seg fault?

asetof...@gmail.com

unread,
May 8, 2015, 12:17:20 PM5/8/15
to
is there In C or in C++
a library for matrices?

Robert Wessel

unread,
May 8, 2015, 1:54:51 PM5/8/15
to
On Fri, 8 May 2015 09:17:10 -0700 (PDT), asetof...@gmail.com
wrote:

> is there In C or in C++
>a library for matrices?


There's always BLAS. There are C versions, and C binding for the
Fortran version. The GNU Scientific Library (GSL) is also fairly
popular. Many others as well.
0 new messages