4

i have some bidimensional arrays like:

int shape1[3][5] =  {1,0,0,
             1,0,0,
             1,0,0,
             1,0,0,
             1,0,0};
int shape2[3][5] =  {0,0,0,
             0,0,0,
             0,1,1,
             1,1,0,
             0,1,0};

and so on.

How can i make an array of pointers to those?

I tried the following, but they don't work (warning: initialization from incompatible pointer type):

int *shapes[]=  {&shape1,&shape2};

int *shapes[]=  {shape1,shape2};

int **shapes[]= {&shape1,shape2};

Any help?

pistacchio
  • 56,889
  • 107
  • 278
  • 420

3 Answers3

6

I believe I just verified what I wrote was correct. The following works as expected:

#include <stdio.h>

int main(int argc, char **argv) {

int shape1[5][3] =  {1,0,0,
                 1,0,0,
                 1,0,0,
                 1,0,0,
                 1,0,0};

int shape2[5][3] =  {0,0,0,
                 0,0,0,
                 0,1,1,
                 1,1,0,
                 0,1,0};

typedef int (*shapes_p)[3];
shapes_p shapes[2] = { shape1, shape2 };

shapes[0][1][0] = 5;
shapes[1][1][0] = 5;

printf("shape1[1][0] == %d\n", shape1[1][0]);
printf("shape2[1][0] == %d\n", shape2[1][0]);

}

The thing to remember is that the type of shape1 and shape2 is actually:

int *shape1[5];

What you have in memory is 3 adjacent arrays of 5 ints each. But the actual type is pointer to array of 5 ints. When you write:

shape1[1][2] = 1;

you're telling the compiler to index to the second array of int[5] and then access the 3rd element of that array. What the compiler actually does is pointer arithmetic on the underlying type pointed to, in this case int[5]. You could do the same with the following code:

int *p = shapes1[0];
p+7 = 1;  // same as shape1[1][2] = 1;

So if you want an array of pointers to int *[5] then you would do:

typedef int (*shapes_p)[5];
shapes_p shapes[2];
Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179
  • -1 sorry -- the type of shape1 is in fact "int shape1[3][5]", it's just that **using the name shape1 in an expression** produces a value of type "int *[5]" (i.e. a pointer to the first element of the array). – j_random_hacker Apr 28 '09 at 19:24
  • 1
    Maybe I'm missing something, but I checked my solution and it works as expected. I added a full compilable example at the beginning of my post. – Robert S. Barnes Apr 28 '09 at 19:52
  • My apologies Robert, you're totally right. You would use a "T *ptr" to access a 1D array of T, so you should use a "T (*ptr)[3]" to access a 2D array of T with rows of width 3. +1. – j_random_hacker May 01 '09 at 04:12
  • I'm looking to clarify an underlying aspect of this question. Unsure if it should be a new question, so I'll start here. This method strictly allocates memory for the pointer (an array of them ultimately, of course), and in no way allocates memory for the underlying arrays of pointers and int, correct? So, the amount of space that `shape1` consumes is not being duplicated? Or, to say it yet another way, the overall type defined by `int (*shapes_p)[5]` and the corresponding memory allocated to it is still only a single pointer? – GG2 Sep 27 '17 at 23:54
3

Updated Fixed type. Thanks j_radom_hacker for bringing this to my attention!

[EDIT: Actually the type here was not correct -- see Robert S. Barnes' answer for the correct type to use.]

Figure out the type of shape1 and shape2 first:

typedef int (*shape_array_t)[5];

Now use this:

shape_array_t sat[] = { shape1, shape2 };
Community
  • 1
  • 1
dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • Hey I made a typo -- sorry about that :P – dirkgently Apr 28 '09 at 19:17
  • @j_random_hacker: I think I fixed it sometime before your comment ;-) – dirkgently Apr 28 '09 at 19:22
  • ACTUALLY, I've read Robert Barnes' answer closely and he's right -- we shouldn't be including the outer subscript ("[3]") in the type of sat. As it stands, to access an individual int with your (or my original) code, you would need to write e.g. "sat[1][0][2][4]" -- the *second* subscript, "[0]", is needed to select the first 3x5 array! – j_random_hacker May 01 '09 at 04:35
  • @dirkgently: I have meanly edited your answer so I could -1 it... Please don't take it personally but it is now clear that this answer (which is currently marked as Accepted) is wrong. (So is/was my own answer, which you should probably downvote.) – j_random_hacker May 01 '09 at 04:39
  • I have just fixed the answer. I am not going to bother myself with downvoting but I will give Robert a +1. – dirkgently May 01 '09 at 04:41
3

First of all, the first array bound refers to the outermost array dimension, so you should probably declare shape1 as:

int shape1[5][3] =  {1,0,0,
                     1,0,0,
                     1,0,0,
                     1,0,0,
                     1,0,0};

and similarly for shape2.

[EDIT: I've changed the type of shapes below to correspond to Robert Barnes' answer -- we don't want the outermost subscript to be included in this type!]

The slightly strange-looking typename you need is:

int (*shapes[])[3] = { shape1, shape2 };

This allows the element at row 4, column 1 of shape2 to be addressed using

shapes[1][3][0]

Breakdown of subexpressions and their C types:

shapes            // has type "int (*x[2])[3]" (decays to "(**x)[3]")
shapes[1]         // has type "int (*x)[3]"
shapes[1][3]      // has type "int x[3]" (decays to "int *x")
shapes[1][3][0]   // has type "int x"

(Note that a dummy x has been included in the types above to make them clearer -- in fact this identifier is not part of the type.)

A rule of thumb for decoding C/C++ types is "starting from the variable name, read right when you can and left when you hit a closing parenthesis." So the decoded typename for shapes is:

An array of pointers to an array of 3 integers.

In general it's much nicer to use typedefs for these complicated types, as dirkgently suggests.

Community
  • 1
  • 1
j_random_hacker
  • 50,331
  • 10
  • 105
  • 169