1

I came across this construction inside a function (e is a parameter passed to the function):

short (*tt)[][2] = (short (*)[][2])(heater_ttbl_map[e]);

and its use (where i is a counter inside a for loop):

(*tt)[i][0]

I think I got the first part of the assignment:

short (*tt)[][2]

for what I understand tt is declared as a pointer to an array of arrays of shorts. The second part is confusing me though, looks like some sort of cast but I'm not sure I understand what it does, expecially this: (*). How does it work?

heater_ttbl_map is declared like this (where pointer1 and pointer2 are both bidimensional arrays of shorts):

static void *heater_ttbl_map[2] = {(void*)pointer1, (void*)pointer2};

as for its use I understand that what is pointed at by tt is dereferenced (and its the content of the third index of the i index of the array, which is a short) but why writing it like this:

(*tt)[i][0]

and not like this:

*tt[i][0]

is it because tt is not an array itself but a pointer to an array?

zakkos
  • 163
  • 1
  • 11
  • 1
    Read about operator precedence. – Iharob Al Asimi Apr 29 '16 at 17:46
  • 2
    This is not an answer, but does [this](http://cdecl.ridiculousfish.com/?q=%28short+%28*%29%5B%5D%5B2%5D%29) help? I find http://cdecl.ridiculousfish.com/ useful at times like this. – davidbak Apr 29 '16 at 17:47
  • I suppose, this is a duplicate of http://stackoverflow.com/questions/3707096/spiral-rule-and-declaration-follows-usage-for-parsing-c-and-c-declarations – SergeyA Apr 29 '16 at 17:48
  • Thank you @davidbak!! It may not be an answer but it's very useful! – zakkos Apr 29 '16 at 19:29

2 Answers2

5

Due to operator precedence ([] has precedence over * operator), there is difference in two statements -

(*tt)[i][0]

In this you access the element at index [i][0] of array to which pointer tt points to .

Whereas, in this -

*tt[i][0]    

First the element at index [i][0](may be 2-d array of pointers) is accessed and then dereferenced.

Using them interchangeably can cause access or dereferencing unauthorized memory location and lead to undefined behaviour.

ameyCU
  • 16,489
  • 2
  • 26
  • 41
  • Thanks @ameyCU! So my intuition about its usage was more or less right. this addresses the second issue but have you any hints for the first, and more tough, part (the declaration)? – zakkos Apr 29 '16 at 19:23
  • @zakkos First part - `(short (*)[][2])(heater_ttbl_map[e]);` . in this `sort (*)[][2]` should be a cast as `heater_ttbl_map` is an array and `heater_ttbl_map[e]` is an bi-dimensional array(_as you pointed in declaration_) . So it must be a case used in the above statement and then `tt` points to this bi-dimensional array . – ameyCU Apr 29 '16 at 19:28
2

As ameyCU explained, the [] subscript operator has higher precedence than the unary * operator, so the expression *a[i] will be parsed as *(a[i]); IOW, you're indexing into a and dereferencing the result.

This works if a is an array of T (or a pointer to T; more on that below). However, if a is a pointer to an array of T, that won't do what you want. This is probably best explained visually.

Assume the declarations:

int arr[3] = { 0, 1, 2 };
int (*parr)[3] = &arr;    // type of &arr is int (*)[3], not int **

Here's what things look like in memory (sort of; addresses are pulled out of thin air):

Address   Item   Memory cell
-------   ----   -----------
                 +---+
0x8000    arr:   | 0 |  <--------+
                 +---+           |
0x8004           | 1 |           |
                 +---+           |
0x8008           | 2 |           |
                 +---+           |
                  ...            |
                 +---+           |
0x8080    parr:  |   | ----------+
                 +---+
                  ...

So you see the array arr with its three elements, and the pointer parr pointing to arr. We want to access the second element of arr (value 1 at address 0x8004) through the pointer parr. What happens if we write *parr[1]?

First of all, remember that the expression a[i] is defined as *(a + i); that is, given a pointer value a1, offset i elements (not bytes) from a and dereference the result. But what does it mean to offset i elements from a?

Pointer arithmetic is based on the size of the pointed-to type; if p is a pointer to T, then p+1 will give me the location of the next object of type T. So, if p points to an int object at address 0x1000, then p+1 will give me the address of the int object following p - 0x1000 + sizeof (int).

So, if we write parr[1], what does that give us? Since parr points to a 3-element array if int, parr + 1 will give us the address of the next 3-element array of int - 0x8000 + sizeof (int [3]), or 0x800c (assuming 4-byte int type).

Remember from above that [] has higher precedence than unary *, so the expression *parr[1] will be parsed as *(parr[1]), which evaluates to *(0x800c).

That's not what we want. To access arr[1] through parr, we must make sure parr has been dereferenced before the subscript operation is applied by explicitly grouping the * operator with parentheses: (*parr)[1]. *parr evaluates to 0x8000 which has type "3-element array of int"; we then access the second element of that array (0x8000 + sizeof (int), or 0x8004) to get the desired value.

Now, let's look at something - if a[i] is equivalent to *(a+i), then it follows that a[0] is equivalent to *a. That means we can write (*parr)[1] as (parr[0])[1], or just parr[0][1]. Now, you don't want to do that for this case since parr is just a pointer to a 1D array, not a 2D array. But this is how 2D array indexing works. Given a declaration like T a[M][N];, the expression a will "decay" to type T (*)[N] in most circumstances. If I wrote something like

int arr[3][2] = {{1,2},{3,4},{5,6}};
int (*parr)[2] = arr; // don't need the & this time, since arr "decays" to type
                      // int (*)[2]

then to access an element of arr through parr, all I need to do is write parr[i][j]; parr[i] implicitly dereferences the parr pointer.


  1. This is where things get confusing; arrays are not pointers, and they don't store any pointers internally. Instead, of an array expression is not the operand of the sizeof or unary * operators, its type is converted from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element of the array. This is why you can use the [] operator on both array and pointer objects.

    This is also why we used the & operator to get the address of arr in our code snippet; if it's not the operand of the `&` operator, the expression "decays" from type "3-element array of int" to "pointer to int"

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Sorry for the late reply, i'm trying to wrap my head around all that! I may still have problems with the casting part and in general why the author decided to go with this very complicated web of, pardon the colloquialism, "things that points/represent other things", instead of going right to the source since, in this case, it's known and always a bidimensional array of shorts. Now, since what we get from `heater_ttbl_map` Is indeed a 2D array, why we need to cast it to a 2D array again? – zakkos Apr 30 '16 at 17:54
  • @zakkos: Without seeing the original code, I can only guess. Casting to `void *` and back again usually implies that you're calling this code through some kind of "generic" interface (like the `qsort` or `pthread_create` functions). – John Bode Apr 30 '16 at 20:10
  • Yes you're right, without knowing the full code it's hard to guess. Just for reference this is taken from a popular open source firmware for 3D printers called Marlin. Back on topic I noticed that what `heater_ttbl_map` returns is not an array but a pointer to a 2D array, now I see the need to dereference it but I still fail to understand why the casting is, IF it is indeed, necessary for the whole thing to work. Shouldn't the dereferencing be enough? Or the cast is needed maybe because `heater_ttbl_map` is an array of pointers that are of type `void` and not of "type" `short[][2]` – zakkos May 01 '16 at 13:20