0

To define a jagged array, I'm using this answer by @FaisalVasi. This works perfectly. To get the (i,j)-entry of a such defined array, type (*jagged[i])[j].

However, I like to put all my arrays in a separate file (my constant arrays), and then I have to declare them in the header file. And I don't manage to do that. I tried **jagged = unsigned[][] and *jagged = unsigned*[], and other attempts I don't remember. Anyway everything I've tried did not work. So how should I declare the jagged array in the header file?

I'm a novice in C and I hope the question is clear. Otherwise please ask me what could I clarify.

Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • *I don't remember what I've tried actually*, you not only have to remember but also post your attempts here. Defining an array in a header file seems like something that you don't want to do except, 1. if you don't know what you're doing or, 2. you **really** know what you're doing. – Iharob Al Asimi Apr 17 '18 at 06:29
  • Sorry @IharobAlAsimi, I badly express myself. *I tried* `**jagged = unsigned[][]` and `*jagged = unsigned*[]`, but I don't remember my other attempts, if there were. – Stéphane Laurent Apr 17 '18 at 06:31

2 Answers2

1

A jagged array in C is usually a pointer to the first element of an array of pointers. Basically a pointer to a pointer. I.e. type **jagged.

To declare just about any variable in a header file use the extern keyword. As in

extern type **jagged;

[Replace type with the actual type]


There's two way to use it:

  1. Full dynamic allocation

    jagged = malloc(sizeof(*jagged) * M);
    for (unsigned i = 0; i < M; ++i)
        jagged[i] = calloc(N, sizeof(**jagged));
    
    // Now jagged is a MxN jagged matrix where each element is zero
    jagged[1][2] = 1;  // Sets a single value to 1
    
  2. Arrays of arrays

    type jagged_row_0[] = { a, b, c };
    type jagged_row_1[] = { x, y };
    
    type **jagged = (type *[2]){ jagged_row_0, jagged_row_1 };
    
    printf("jagged[1][0] = %d\n", jagged[1][0]);
    

Of course, you could make an actual array of array of pointers instead (much like the second case above):

extern type *jagged[];

...

type *jagged[] = { jagged_row_0, jagged_row_1 };

...

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

Be very careful when having rows with different size though, so you don't go out of bounds.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I get an error at the compilation if I do that: `error: subscripted value is neither array nor pointer nor vector` in `(*jagged[i])[j]`. This is the same error I got with all my attemps. Maybe `(*jagged[i])[j]` is not the appropriate way to get the (i,j)-entry ? – Stéphane Laurent Apr 17 '18 at 06:39
  • @StéphaneLaurent "vector"? Are you really programming in C++? Then you should use [`std::vector`](http://en.cppreference.com/w/cpp/container/vector). – Some programmer dude Apr 17 '18 at 06:40
  • No, this is C, not C++ (compiled with gcc). – Stéphane Laurent Apr 17 '18 at 06:41
  • @StéphaneLaurent As for the expression `(*jagged[i])[j]` think about it a little while... `jagged` is a pointer to pointer. `jagged[i]` is a pointer. `*jagged[i]` is the *value* `jagged[i]` is pointing to. In other words, you try to use a *value* as a pointer or array. The correct way is simply `jagged[i][j]`, once properly initialized of course. – Some programmer dude Apr 17 '18 at 06:41
  • @StéphaneLaurent The `gcc` front-end program can compile C++ files as well. What is the filename suffix of your source? Is it plain `.c`? – Some programmer dude Apr 17 '18 at 06:43
  • @StéphaneLaurent For this `jagged`, you need to `malloc` and then `for ... malloc` to **create** the "matrix"; and, `for ... free` and then `free` to **destroy** it. Usually. – Stan Apr 17 '18 at 06:43
  • @Someprogrammerdude Yes this is plain `.c`. But in fact, this is some C code compiled through Haskell, so I don't know exactly what it does. – Stéphane Laurent Apr 17 '18 at 06:47
  • @Someprogrammerdude in `type **jagged` you are missing a further level, see my explanation above – Marco Pantaleoni Apr 17 '18 at 07:01
  • @MarcoPantaleoni That makes no sense to me. The OP have `**jagged = unsigned[][]` in the question, which tells me the OP wants a matrix or "2d" jagged array, which e.g. `type **jagged` could be. Adding an extra indirection level just makes no sense. – Some programmer dude Apr 17 '18 at 07:04
  • @Someprogrammerdude I never said I have `**jagged = unsigned[][]`. I said I *tried* `**jagged = unsigned[][]` in the header file. What I have is `unsigned (*jagged[])[]`, as in the link I posted at the beginning. Sorry if I badly expressed myself. – Stéphane Laurent Apr 17 '18 at 07:11
  • @StéphaneLaurent Still doesn't make sense, why the extra indirection of having an array of pointers to arrays? Instead of e.g. an array of pointers? See e.g. the end of my updated answer for a much simpler variant. – Some programmer dude Apr 17 '18 at 07:21
  • @Someprogrammerdude I wrote I agree with you, but actually not, I don't :-) And the reason is that using your method creates actually a bi-dimensional classic array, not a jagged one. When doing `type **jagged = (type *[]){ jagged_row_0, jagged_row_1 }` the compiler expands the rows and **pads them to the same size**. Verify yourself using sizeof() for all the rows... So this is not a jagged array. – Marco Pantaleoni Apr 17 '18 at 08:04
  • @MarcoPantaleoni No that's not true. Remember that arrays decays to *pointers* to their first element. That means if you do e.g. `sizeof(jagged[0])` what you get is the size of the *pointer* and not the array whose first element it points to. There's no expansion or padding done by the compiler. – Some programmer dude Apr 17 '18 at 08:06
  • @Someprogrammerdude doh, you're totally right, my brain is on strike today! – Marco Pantaleoni Apr 17 '18 at 08:27
1

NOTE: Deviating from the requested array-of-pointers-to-rows syntax and pointing to the rows directly in the array, as proposed by @Someprogrammerdude, allows to obtain the same result, but with one less indirection and with a more clear access syntax.

direct array of rows solution

definition

unsigned jagged_row0[] = { 0, 1, 99 };
unsigned jagged_row1[] = { 1, 2, 3, 4, 5, 6, 99 };

unsigned *jagged[] = (unsigned *[]){ jagged_row0, jagged_row1 };

or in general:

type jagged_row0[] = { ... };
type jagged_row1[] = { ... };
...

type *jagged[] = (type *[]){ jagged_row0, jagged_row1, ... };

declaration

extern unsigned *jagged[];

or in general:

extern type *jagged[];

usage

unsigned v_i_j = jagged[i][j];

or in general:

type v_i_j = jagged[i][j];

original answer

The following solution addresses the definition given in the cited answer by @FaisalVasi, where the jagged array stores explicit pointers to the jagged rows.

definition (in some .c file)

unsigned jagged_row0[] = {0,1};
unsigned jagged_row1[] = {1,2,3};

unsigned (*jagged[])[] = { &jagged_row0, &jagged_row1 }; /* note the ampersand */

/* or alternatively, since compound literals are lvalues ... */
unsigned (*jagged[])[] = { &(int[]){0,1}, &(int[]){1,2,3} };  

declaration

extern unsigned (*jagged[])[];

usage

unsigned *jagged_row;
...
jagged_row = *jagged[i];
unsigned v_i_j = jagged_row[j];  /* value at [i][j] */

or more compactly:

unsigned v_i_j = (*jagged[i])[j];  /* value at [i][j] */

explanation

A jagged row is an array of some basic type, in our case an array (of length determined by the static initialization) of unsigned (unsigned[]), which can be thought of, with some caveats, as a pointer to unsigned (unsigned *).

With the proposed definition, the jagged array is an array of pointers to jagged rows, which, with the same simplification, can be though of as an array of unsigned **.

When you index the first dimension, you are getting the pointer to the jagged row (an array), then you have to dereference this pointer to get to the array itself that is the jagged row, than you have to index this array to get to the final value.

Marco Pantaleoni
  • 2,529
  • 15
  • 14