Setting Pointer to Arbitrary Dimension Array

Arbitrary Dimensional Array

Haven't compiled anything, just visual inspection. What about this:

template<int array_length>
class ArbitraryArray{
public:
int array[array_length];
ArbitraryArray ** subArray;

ArbitraryArray(){}

ArbitraryArray(int depth){
if (depth == 1)
subArray = 0;
else {
subArray = new ArbitraryArray*[array_length];
for (int i = 0; i < array_length; i++)
subArray[i] = new ArbitraryArray(depth - 1);
}
}
};

Does the C standard permit assigning an arbitrary value to a pointer and incrementing it?

The assignment:

void *ptr = (char *)0x01;

Is implementation defined behavior because it is converting an integer to a pointer. This is detailed in section 6.3.2.3 of the C standard regarding Pointers:

5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined,
might not be correctly aligned, might not point to an entity
of the referenced type, and might be a trap representation.

As for the subsequent pointer arithmetic:

ptr = (char *)ptr + 1;

This is dependent on a few things.

First, the current value of ptr may be a trap representation as per 6.3.2.3 above. If it is, the behavior is undefined.

Next is the question of whether 0x1 points to a valid object. Adding a pointer and an integer is only valid if both the pointer operand and the result point to elements of an array object (a single object counts as an array of size 1) or one element past the array object. This is detailed in section 6.5.6:

7 For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a
pointer to the first element of an array of length one with the type
of the object as its element type

8 When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer
operand. If the pointer operand points to an element of an array
object, and the array is large enough, the result points to an element
offset from the original element such that the difference of the
subscripts of the resulting and original array elements equals the
integer expression. In other words, if the expression P points to the
i-th element of an array object, the expressions (P)+N (equivalently, N+(P) ) and (P)-N (where N has the value n ) point to,
respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an
array object, the expression (P)+1 points one past the last element of
the array object, and if the expression Q points one past the
last element of an array object, the expression (Q)-1 points to
the last element of the array object. If both the pointer
operand and the result point to elements of the same array
object, or one past the last element of the array object, the
evaluation shall not produce an overflow; otherwise, the behavior is
undefined.
If the result points one past the last element of the
array object, it shall not be used as the operand of a unary
* operator that is evaluated.

On a hosted implementation the value 0x1 almost certainly does not point to a valid object, in which case the addition is undefined. An embedded implementation could however support setting pointers to specific values, and if so it could be the case that 0x1 does in fact point to a valid object. If so, the behavior is well defined, otherwise it is undefined.

How can I work with dynamically-allocated arbitrary-dimensional arrays?

There is a data structure used in the implementation of the J language (a dialect of APL) which accommodates dynamically-allocated arbitrary-dimensional arrays. It uses a hybrid of a struct and a dynamic array for its data structure, a trick commonly known as the struct hack. (More info on the J implementation here and here.)

To see the idea in a simple context, consider the 1D case: we want a dynamic 1D array which carries its size along with it. So:

struct vec { int n; int p[]; };

Since the p member is last, and C has no built-in bounds checking, it can be used to access additional memory off the end of the struct. Of course, when allocating, we'll need to provide this extra memory and not simply allocate the size of the struct. The struct is just the header of the array. C90 requires a number (say, 1) for the length of the p[] array, but C99 allows the number to be omitted so the size of the header is more straightforward to calculate.

So, an array with more dimensions will need more values to hold the sizes of each dimension. And for our structure to accommodate arrays of different dimensionality, this dimension vector will need to be variable-length as well.

What we can do to achieve all of this is to apply the struct hack twice, recursively upon itself. This gives us a memory layout like this, where R is the number of dimensions which we'll call the rank of the array, the D values are the lengths of each dimension, and the V values are the actual array data:

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

And to describe this in C,

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

The elements of an array a immediately follow the R elements of the dims vector D. So the V elements can be accessed at a->d[r+0], a->d[r+1], ... a->d[r+i] (after reducing the index vector down to a single index on the flattened representation). The elements are easiest to treat in row-major order. The number of actual elements is the product of all the dimensions, all the dimensions multiplied together. Edit: The expression here might be better written: (a->d+a->r)[0], (a->d+a->r)[1], ... (a->d+a->r)[i].

In order to allocate one of these things, we'll need a function to compute this product as part of the size calculation.

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

And to initialize, we just need to fill-in the members.

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

Remembering the formula for accessing 2D data (say an array of [m][n] elements) with a single index (it's a typical dynamic array like in the question). Element [i][j] is at i×n+j. With a 3D array [m][n][o], element [i][j][k] is at i×(n×o)+j×o+k.

So we can calculate a single index for our linearly-laid-out data from an array of indices and the array of dimensions.

int *elem(arr a, ...){
va_list ap;
int idx = 0;

va_start(ap,a);
if (a->r){
idx = va_arg(ap,int);
for(int i=1; i<a->r; i++){
idx *= a->d[i];
idx += va_arg(ap,int);
}
}
va_end(ap);

return &a->d[a->r + idx];
}

Don't forget the headers:

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

And voilà:

int main() {    

{
int i,n=6;
arr m = makearr(1, (int[]){n});
for (i=0;i<n;i++)
*elem(m,i) = i;
for (i=0;i<n;i++,printf(" "))
printf("%d",*elem(m,i));
}
puts("\n");

{
int i,j,n=4;
arr m = makearr(2, (int[]){n,n});
for (i=0;i<n;i++)
for (j=0;j<n;j++)
*elem(m,i,j) = i*n+j;
for (i=0;i<n;i++,printf("\n"))
for (j=0;j<n;j++,printf(" "))
printf("%d",*elem(m,i,j));
}
puts("\n");

{
int i,j,k,n=3;
arr m = makearr(3, (int[]){n,n,n});
for (i=0;i<n;i++)
for (j=0;j<n;j++)
for (k=0;k<n;k++)
*elem(m,i,j,k) = (i*n+j)*n+k;
for (i=0;i<n;i++,printf("\n"))
for (j=0;j<n;j++,printf("\n"))
for (k=0;k<n;k++,printf(" "))
printf("%d",*elem(m,i,j,k));
}

return 0;
}

Output:

0 1 2 3 4 5 

0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15

0 1 2
3 4 5
6 7 8

9 10 11
12 13 14
15 16 17

18 19 20
21 22 23
24 25 26

Further elaboration of this code to support 2D matrix multiplication and column slices has been posted to comp.lang.c in this thread.

Better-motivated question/more powerful data structure.

C pointer to 2 dimensional array

Two-dimensional arrays != double pointers.

You almost certainly need dynamic memory allocation for this. You also want to deep copy the contents of the array - it's a non-static local variable, so it's invalid out of its scope. You can't do TYPE arr[sz]; return arr; as a consequence.

const size_t width = 3;
const size_t height = 5;
TimeSlot tmpTimeSlot[width][height];

systemMatrix = malloc(width * sizeof systemMatrix[0]);
for (int i = 0; i < width; i++) {
systemMatrix[i] = malloc(height * sizeof systemMatrix[i][0]);
for (int j = 0; j < height; j++) {
systemMatrix[i][j] = tmpTimeSlot[i][j];
}
}

How to set arbitrary size array in ctypes?

It appears the missing ingredient was to use the function cast in ctypes. It seems as though ctypes does some generic casting itself automatically in simple cases, but when presented something more difficult (like a struct built of two other stucts), it doesn't seem to automatically cast.

data   = D()
function(pointer(data))
size = data.A #returns 250
buf = ARRAY(BYTE,size)
data.B = pointer(buf)
buf = cast(buf,POINTER(D))
function(pointer(data))

This method even appears to work through a c_void_p; in which case you can forcibly use C functions without needing to explicitly define the exact structure they need in ctypes.

Two-dimensional dynamic array pointer access

I'll assume m is the number of columns, and n the number of rows (you can use n instead of m in my answer if it's the opposite).

In order to access the 2D array, you need 2 indices - let's call them x and y:

x index will be in the range 0 .. m-1, and

y index will be in the range 0 .. n-1

You can calculate the index for your p array in the following way:

int p_idx = y * m + x

Then you can access your arrays element e.g. this way:

p[p_idx] = 111;   // set an element value
int a = p[p_idx]; // get an element value


Related Topics



Leave a reply



Submit