2

I know that, for the following:

int a[10];

a is a pointer of the type int * to a[0], while &a is a pointer of type int (*)[10].

Now my question is for the following 2D array:

int b[20][30];
  1. Is b a pointer of the type int **? Or is it a pointer of the type int (*)[30]?

  2. Is &b a pointer of the type int (*)[20][30]?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Kevin Richards
  • 570
  • 1
  • 6
  • 23
  • 3
    No no no. `a` is an array. `b` is an array of arrays. Neither is a pointer. Read about [arrays](http://stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c). – chris Jul 16 '14 at 20:10
  • 3
    SO Rule of thumb #2: "I know that X" is usually followed by a complete misunderstanding. – Kerrek SB Jul 16 '14 at 20:10
  • “I know, that for the following […]”—in most circumstances, yes (e.g. as operand to `sizeof` is a notable exception); ad 1.: It's `int (*)[30]`; ad 2.: Yes. Look at the link Chris provided. – mafso Jul 16 '14 at 20:13
  • 1
    `a is a pointer of the type int *` That is one mighty large pointer when I issue a `sizeof(a)`. – PaulMcKenzie Jul 16 '14 at 20:29

4 Answers4

3

First of all make it clear that arrays are not pointers. Relationship between pointers and arrays is subtle. I would suggest you to read second chapter of tutorial on pointers by Ted Jensen first.

I would like to tell you something in a nutshell what has been describe in this chap. Consider following example.

int a[10];
int *p;

Now you can write

p=a; 

that is equivalent to

p=&a[0];

This thing make many texts to say array is name of pointer. But it is better if you say "the name of the array is the address of first element in the array" .

Because though you can write

p=a;

but you can not write

a=p;

Now come to your question:

From above discussion it should be clear that b is not pointer of type int** .For example:

int b[10][10];
int **x;
int *p;

b=&p; // this is error

x=&p; // this fine

For your other questions you may use online CDECL.

if you write

int b[10][10]; --> declare b as array 10 of array 10 of int

int (*p)[10]; --> declare p as pointer to array 10 of int

int (*p)[10][20]; --> declare p as pointer to array 10 of array 20 of int

A.s. Bhullar
  • 2,680
  • 2
  • 26
  • 32
3

Arrays are not pointers (this point cannot be stressed enough).

That being said, an array decays to a pointer to its first element. For example:

int a[10];
int b[20][30];

void print_a(int *);
void print_b(int (*)[30]);

print_a(a);
print_b(b);

The first element of a is a[0], and similarly the first element of b is b[0]. You basically take that first dimension away, and change it to a (*); I'll explain more in a moment since it is a bit more complex than that.

The relationship between pointers and arrays is riddled with contextual subtleties that aren't terribly difficult to grasp, but the size information in various scopes makes it interesting and also helps to give you an idea of how the decay works:

#include <stdio.h>
int h(int *pa)
{
    printf("h(int *): sizeof pa=%zu\n", sizeof pa);
    printf("h(int *): sizeof *pa=%zu\n", sizeof *pa);
    return *pa;
}
int g(int (*pa)[5])
{
    printf("g(int (*)[5]): sizeof pa=%zu\n", sizeof pa);
    printf("g(int (*)[5]): sizeof *pa=%zu\n", sizeof *pa);
    return h(*pa);
}
int f(int (*pa)[3][5])
{
    printf("f(int (*)[3][5]): sizeof pa=%zu\n", sizeof pa);
    printf("f(int (*)[3][5]): sizeof *pa=%zu\n", sizeof *pa);
    return g(*pa);
}
int main(void)
{
    int arr[2][3][5] = {{{11235}}};
    printf("main: sizeof arr=%zu\n", sizeof arr);
    printf("main: sizeof *arr=%zu\n", sizeof *arr);
    printf("%d\n", f(arr));
}

Every pointer is the same size (this may not always be true on all platforms!), but by dereferencing the pointer, you see the size of a given element of the array, whether you dereference using the unary * operator or the [N] array notation, which is equivalent to *((array)+(N)) by definition.

Anyway, going back to the difference between pointers and arrays, you should understand that int[20][30] is not the same as int **. Why is that? Because of the fact that int[20][30] decays to a pointer of type int(*)[30], no more decay can occur until the pointer is dereferenced. Moreover, int ** is actually int *(*), which means it can point to the first element of an array of pointers. That is, int ** might have once been int *[N].

int foo[x][y][z] <=> int (*foo)[y][z]

int *foo[m][n] <=> int *(*foo)[n]

int (*foo[a])[b] <=> int (**foo)[b]

In the first case, we have a 3-D array, which decays to a pointer to a 2-D array; in other words, an array of arrays and a pointer to an array are closely related and interchangeable in many contexts aside from the size issue. The first dimension x is the one that decays, leaving the y and z dimensions.

In the second case, we have a 2-D array of pointers. This decays to a pointer to an array of pointers. Again, an array of arrays is closely related to a pointer to an array, and dimension m decays, leaving dimension n.

In the third case, we have an array of pointers to arrays. It decays to a pointer to a pointer to an array. Since dimension a is closest to the variable name, that is the one that decays, leaving dimension b. Note that since it is an array of pointers to arrays, the pointers could point to the first element of arrays themselves:

int arr[2][3][5];
int (*foo[2])[5] = { arr[0], arr[1] };
int (**foo_ptr)[5] = foo;

Recap:

  • Array (size A) of arrays (size B) <=> Pointer to array (size B)
  • Array (size A) of pointers <=> Pointer to pointer
  • The array that decays/grows is always the innermost array/pointer, the innermost being the one closest to the variable name in the variable declaration, with arrays having higher associativity than pointers, though of course parentheses make all the difference.

This rabbit hole obviously can be confusing, but I hope I helped a bit at least!

1

No, a is not of type int*, it is of type int [10] (i. e. of an array type). This is why sizeof(a) will give you the size of the array (40 bytes, assuming 32 bit integers).

Likewise, b is of type int [20][30], which is nothing other than an array of arrays. I. e. sizeof(b[0]) is the size of one line array, which is 120 in this case, the size of the entire array (sizeof(b)) is 20 times the size of the line arrays, which is 2400 bytes in this case.

The trick is, that an array decays into a pointer to its first element in almost all contexts. So when you do pointer arithmetic (as in b[3]) on the 2D array, b decays into a pointer of type int (*)[30], so the pointer arithmetic skips rows, adding three times the size of a row (360 bytes in this case) - the rows are the elements of the 2D array. The resulting type of b[3] is int [30], i. e. the dereferenced pointer.

Once you have dereferenced to a row array with b[3], you can again invoke pointer arithmetic to select the correct element in this row (b[3][5]). Again, the array-pointer-decay is invoked, the mechanic is the same.

Note that there is no pointer array involved as is the case when you emulate a 2D array with an int**. The double dereference b[3][5] translates into something like ((int*)b)[3*30 + 5] by virtue of array-pointer-decay, only the element itself is accessed from memory.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • `int ()[10]` is a function type with no or unspecified arguments returning an array of 10 ints. `a` is an `int [10]` -- an array of 10 ints. – Chris Dodd Jul 16 '14 at 20:50
  • I don't like the last paragraph; the right code would be `((int *)b)[3*30+5]` and this is not "by virtue of decay" – M.M Jul 17 '14 at 05:08
  • @MattMcNabb Included your suggestion for more precise code, thanks. I was writing this in a bit of a hurry, yesterday, sorry for the imprecision. But I stand by the phrase "by virtue of array-pointer-decay": The decay is the mechanism that make this translation happen - it is the thing that makes the double dereference `b[3][5]` behave so radically different depending on whether `b` is of type `int [20][30]` or of type `int**`. – cmaster - reinstate monica Jul 17 '14 at 16:39
0
int* temp;
int arraySize = 20;
temp = (int *)malloc(arraySize * sizeof(int));

This will create a section in memory 20 "ints" long, similarly as you mentioned.

int** temp;
int arraySize = 20;
int rowSize = 10;
temp = (int **)malloc(arraySize * sizeof(int *));
for(i=0; i<arraySize; i++){
   temp[i] = (int *)malloc(rowSize * sizeof(int));
}

That is what the 2D array would actually look like.
temp[0] would give you the address of the first "array". In which you could do something like above int *array = temp[0] then access it like a normal array but using *array[0] to get the value.

2D arrays really don't mesh well with pointers and saying *temp[0] to get the values of the first array. You can try to mess with it, you'll figure it out, but don't have a machine that can compile C with me right now.

Reference that may help: http://www.cs.swarthmore.edu/~newhall/unixhelp/C_arrays.html

Hashman
  • 151
  • 5