26

I am modifying some code and came across a declaration that I am having trouble understanding:

int *userMask[3][4] = {0};

What exactly is this pointing to? Is it a matrix where every element is a pointer? Or is it pointing to a matrix of size [3][4]?

Thanks


I guess my question is how userMask[2][maskElement][user] can work when it is declared as int. Wouldn't userMask have to be int[] for that to work properly? I must not be understanding this right...

On a side note, thanks for your suggestion about cdecl Robert. However, does anyone know how to use it in an XP command prompt? All I can get is syntax error :(

razlebe
  • 7,134
  • 6
  • 42
  • 57
Tim
  • 1,814
  • 8
  • 29
  • 41

9 Answers9

43

Short answer

Given userMask is declared as

int *userMask[3][4];

then userMask has type int*[3][4]. It's a 2d array of pointers to int. The size of the outer dimension is 3, the size of the inner dimension is 4. Really that is nothing more than a 3-element 1d array which element type is another 4-element 1d array which element type is int*.

Steps explained

So if you do

userMask[2][maskElement][user]

then essentially with the first two indices you pick the particular pointer out of the 2d array:

int * p = userMask[2][maskElement];

then you pick an int somewhere offset from that pointer by doing

p[user]

now that code is all in userMask[2][maskElement][user].

Valid C Code

To do it step by step with valid c code (don't worry if you don't understand everything yet in the following):

int * userMask[3][4] = { { 0 } };
int ** pa = userMask[2]; /* int*[4] becomes int** implicitly */
int * pi = pa[maskElement];
int i = pi[user];

assert(i == userMask[2][maskElement][user]);

Difference between Arrays and Pointers

So i think i show you something important. The array above does not contain pointers to arrays. Lets look how different they behave, which many c programmers don't expect:

int array[5][4][3];
/* int[4][3] implicitly converts to int(*)[3] (pointer to first element) */
int (*parray)[3] = array[0]; 
int ** pint = (int**) array[0]; /* wrong!! */

Now, what will happen if we do parray[1] and pint[1] ? The first will advance parray by sizeof(int[3]) bytes (3 * sizeof(int)), the second will advance by only sizeof( int* ) bytes. So actually while the first gives you the correct array array[0][1], the second will give you ( char * )array[0] + sizeof( int* ), which is somewhere we don't really want it to be. But grabbing the wrong offset is not all about it. Because it doesn't know an array is accessed, it will try to interpret what is at pint[1] as an int*. Say your array was initialized with 0x00. Then it will do the next index step based off address 0x00 (Doing pint[1][0] for example). Oh noes - utterly undefined behavior! So it's really important to stress the difference.

Conclusion

This was more than you asked for, but I think it's quite important to know these details. Especially if you want to pass 2d arrays to functions then this knowledge is really useful.

Lii
  • 11,553
  • 8
  • 64
  • 88
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • The OP asked about C, not C++. – Robert Gamble Nov 08 '08 at 17:55
  • 1
    oops, haven't noticed. im sorry, i'll read more careful next time :) – Johannes Schaub - litb Nov 08 '08 at 17:58
  • If that is the case, I am still slightly confused. The userMask array is being used later as: if(userMask[2][maskElement][user] && blah) result = true; How can that pointer to an int be referenced by [user] like this? It is not an int array, it is an int, correct? – Tim Nov 08 '08 at 16:17
18

This is a two-dimensional array where each element is a pointer to an int, and all the pointers are initialised to zero.

In your follow-up, you show that the array is used like this:

if(userMask[2][maskElement][user] && blah)
    result = true;

In this case, each element in userMask should actually point to an array of ints. (An int* can point to a single int or an array of ints). To determine this, check the code that assigns values to userMask. For example, it is possible to write:

int userArray[2] = { 10, 20 };

userMask[0][0] = userArray; // userMask[0][0] points to the
                            // first element of userArray.

Then the following code indexes into userArray:

int value = userMask[0][0][1]; // sets value = userArray[1], giving 20.
ChrisN
  • 16,635
  • 9
  • 57
  • 81
12
int *userMask[3][4] = {0};

is a 2-dimensional array where each member is a pointer to int. Additionally, all members are initialized to null pointers.

int (*userMask)[3][4];

would be a pointer to a 2-dimensional array of ints. Brackets in C bind tighter than * so the parenthesis are needed to create a pointer to an array.

cdecl is a simple utility you can download to explain complex declarations:

cdecl> explain int *userMask[3][4]
declare userMask as array 3 of array 4 of pointer to int

It can also do the opposite:

cdecl> declare userMask as pointer to array 3 of array 4 of int
int (*userMask)[3][4]
Robert Gamble
  • 106,424
  • 25
  • 145
  • 137
  • All members are null without initialization. All members are initialized to 0. There is a difference, even if it is a fine one. – tloach Nov 08 '08 at 16:54
  • 1
    @tloach, I'm sorry but none of what you said makes any sense to me. C doesn't automatically initialize variables unless they are static and I don't know what you think the difference is between initializing pointers to 0 or null is but there is none. – Robert Gamble Nov 08 '08 at 16:59
3
if(userMask[2][maskElement][user] && blah)
   result = true;

The second part here is that there are no arrays in C; there is only pointer arithmetic. By definition, p[i] is always equivalent to *(p+i) so

userMask[2][maskElement][user]

is equivalent to

*((userMask[2][maskElement])+user)

The code is somewhere assigning a vector (I'd bet money it's from a malloc(3c) or a similar call) to the pointer in that array; now your if is saying

IF the user-th element of the vector at userMask[2][maskElement] is non-zero

THEN IF blah is non-zero (because of &&'s short circuit evaluation, the second conjunct doesn't get evaluated if the first conjunct is 0)

THEN set result = true.

Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
2

Apply the inside-out rule.

int *userMask[3][4] = {0};

Starting at the innerpost part of the declaration,

userMask

is the name

userMask[3] 

allocates space for (is a vector of) 3 of them

userMask[3][4] 

allocates space for 4 userMask[3]'s

int *

tells us that userMask items are type pointer to int

and then = {0} is an initializer where all elements are 0. So

int *userMask[3][4] = {0};

is a 3x4 array of int *, initialized to 0.

Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
0

I think that statement accesses the third row of the usermask array, then accesses the maskElement'th pointer in that row, and since that is an int pointer, it can point to the beginning of an int array (think character strings), which I assume it is doing, and that array is sub-indexed by user.

Cameron
  • 96,106
  • 25
  • 196
  • 225
0

userMask[2] is of type int*[],
userMask[2][maskElement] is of type int*,
and so userMask[2][maskElement][user] is of type int.

The declaration

int *userMask[3][4] = {0};

is shorthand for

int *userMask[3][4] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

where each of the zeros is implicitly converted to (int*)0.

pdc
  • 2,314
  • 20
  • 28
0

it helps if you read it like this:

type variablename[array_spec];

in this case: int* usermask[3][4];

so its a matrix of int*.

now, since c does not differentiate between pointer and arrays, you can use array indexing on pointers.

int* i;
int the_int_behind_i = *(i+1);
int also_the_int_behind_i = i[1];

This needs i to point to an area where several ints are lined up behind each other, of course, like an array.

Note that the index operator[] used in the last examples looks like the array_spec in the first, but the two are quite different.

So: userMask[2][maskElement][user]

picks the usermask pointer stored at [2][maskElement], and evaluates it for the user user

JK.
  • 21,477
  • 35
  • 135
  • 214
tabdamage
  • 260
  • 1
  • 3
-1

It is a matrix where every element is a pointer.

If it was pointing to a matrix of size [3][4] the code would have been

int userMask[3][4]={0};
martjno
  • 4,589
  • 5
  • 34
  • 31
  • If it was to _point_ to a matrix of size [3][4], it must be int (*userMask)[3][4] = 0; This is a pointer to a matrix of size [3][4], initialised to NULL. – Sundar R Nov 08 '08 at 16:16