Initializer for array of pointers in C multiarray

Viewed 69

When building a multi-index array in C, it is possible to leave "open" the first index, either via a pointer

#define A 3
#define B 5
#define C 2

double aux[A][B][C] = {[0 ... A-1][0 ... B-1][0 ... C-1]=1.0};
double (*s)[B][C] = aux;

or simply with the array notation (but in this case I do not know how to use the auxiliary variable)

double s[][B][C] = {[0 ... A-1][0 ... B-1][0 ... C-1]=1.0};

Now, if we want to leave open an intermediate index, s[A][][C], I think the only solution is to declare an array of pointers, isn't it? For A=3, the following two initializations work

double (*t[A])[C] =  {aux[0],aux[1],aux[2]};
double (**u)[C] =  t;

and we can use t[i][j][k] or u[i][j][k] in the main code.

But consider A = 100. Is there some way to declare all the aux[0...A-1] pointers in a compact way in the initializer? , or somehow "cast" aux into t or into u?

Note: as Jonathan points out, triple-dot is a gcc extension. For this question, well, I am happy to initialize to zero anyway, or having a random aux. An answer just reserving memory, such as

double aux[A][B][C];
double (*t[A])[C] =  (miraculous cast) aux[];
double (**u)[C] =  t;

would be enough, if triple-dots and initializers can not help.

possible hint?: If I do

double *trivial[A] = {aux[0],aux[1],aux[2]};
double (**v)[C] =  trivial;

I get an interesting (multiple) warning, repeated for each assignment in the initializer

warning: incompatible pointer types initializing 'double *' with an expression of type 'double [5][2]' [-Wincompatible-pointer-types]
double *trivial[A] = {aux[0],aux[1],aux[2]};
1 Answers

Is there some way to declare all the aux[0...A-1] pointers in a compact way in the initializer?

No, you'd run a little loop like

for (int i=0; i<A; i++) t[i] = aux[i];

and though it looks like this is doing 'more work', in fact any compiler worth its salt will make this just as fast as a really compact-looking initializer. In other words, if there was a compact notation for this kind of thing, the actual assignment steps would still have to be done, and they would be the same as this loop.

, or somehow "cast" aux into t or into u?

Well, here's the thing - sorry if this is trivial to you but I think it is relevant. Whenever you use aux in your code after its definition, it is automatically turned into the relevant pointer. So you may wish to have a pointer t[2] to the start of the last 5x2 block of doubles of your array, but aux[2] is exactly that. As the C18 standard says in §6.3.2.1, item 3:

an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object

so the 5x2 array aux[2] is turned into a pointer-to-array-of-2-doubles, like the ones you were storing in t.

If you #include <assert.h> and put this in your code,

assert(t[1] == aux[1]);
assert(u[1] == aux[1]);
assert(t[1][3] == aux[1][3]);
assert(u[1][3] == aux[1][3]);

there will be no 'Assertion failed' errors. So you might as well start with t = aux; or u = aux; or just use aux itself whenever you need the appropriate pointer.

Regarding the 'possible hint' from your compiler warning: the statement

double *trivial[A]

declares trivial as an array of 3 pointers-to-double. The initialization then tries to assign to trivial[0] (a pointer-to-double) the value of aux[0], a pointer-to-array-of-2-doubles, hence the warning. That's why you can't omit the ()[C] from your original version.

Related