3

I would have thought that an integer array is of type pointer to integer, so that would mean that a pointer to an integer array is of type double pointer to integer. But the results that I am getting say otherwise. I have suspicion that the type integer array is not of type pointer to integer.

Here is my example:

int main(){
  int p[3]={1,2,3};
  int (*ptr)[3] = &p;
  int **ptr2 = &p;

  printf("%d\n",(*ptr)[0]);
  printf("%d\n",**ptr2);
  return 0;
}

p is of type 3 element integer array.

&p is of type pointer to 3 element integer array.

ptr is of type pointer to 3 element integer array.

ptr2 is of type double pointer to integer

So my question is, if an integer array is a pointer to integer, why doesn't ptr2 work as expected? Is the type double pointer to integer different from the type pointer to integer array? Any help clearing this up for me would be appreciated. Thank you!

3
  • 4
    An array is not a pointer. It will decay to a pointer to its first element when used as a value. Also, taking the address of an array gives a pointer to the first element of that array, not a pointer to some memory location with the address of the first element o the array. Make sure that your warnings are turned on, because int **ptr2 = &p; should cause some since you're assigning an int * to an int **. Commented Jun 12, 2019 at 17:06
  • &p is the same as p in this case Commented Jun 12, 2019 at 17:09
  • 1
    @alinsoar The value may be the same, but the type is not. Commented Jun 12, 2019 at 17:13

3 Answers 3

3

While the innermost dimension of a multidimensional array in C readily converts to a pointer:

char (*p)[2][3][4]= //<pointer to an array of 2 or array of 3 of array of 4 char
    (char[5/*<- this one decays to a ptr*/][2][3][4]){0};
    //^array 5 of array 2 of arry 3 of array 4 of char
    ;

arrays and pointers are very different in C despite the confusingly identical syntax for indexing and dereferencing (which are both two sides of the same coin as: x[index] is the same as *(x+index) or index[x]).

I think this is a part of C where if you don't have context, the idea that the language maps directly to assembly breaks down most apparently.

Compare

char a[1][1][1][1]={{{{'a'}}}}; //1 byte
char ****b = &(char***){&(char**){&(char*){&(char){'b'}}}}; //1byte+4*ptr_sz

and now the code that ****a generates vs what ****b generates:

char get_a_char(void)
{
    return ****a;
}
char get_b_char(void)
{
    return ****b;
}

x86-64:

get_a_char:
        mov     al, BYTE PTR a[rip]
        ret
get_b_char:
        mov     rax, QWORD PTR b[rip]
        mov     rax, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax]
        mov     al, BYTE PTR [rax]
        ret

When you dereference a multiply indirect pointer (b), you get a pointer chase.

When you dereference/subscript a multidimensional array, then your subscripts (zeros if you're just dereferencing) and the dimensions of the array are used to calculate an offset from a base, so you eventually either get an offsetted pointer (the same pointer if you're just derefing, just with a different type) if you deref/subscript through just some of the dimensions, or the same followed by a fetch from that address if you deref/subscript through all of them.

In your case ptr is int (*)[3] -- a pointer to an array of 3 int but ptr2 is int** -- a pointer to a pointer to int.

When you do ptr2[1][2] you add 1 pointer size, fetch a pointer from there, and then add 2 int (target type) sizes to the fetched pointer and fetch from there.

That's very different from when you do ptr[1][2] in which case you add one int[3] size to the base pointer and then 2 int sizes and fetch from there (a total of 1 fetch).

The types of ptr obviously cannot be compatible with the type of ptr2.

Sign up to request clarification or add additional context in comments.

Comments

2

I would have thought that an integer array is of type pointer to integer,

It is not. Arrays decays to pointers in many common circumstances, but they are not the same.

so that would mean that a pointer to an integer array is of type double pointer to integer.

No it is not.

why doesn't ptr2 work as expected?

ptr2 is a pointer that contains the address of the array p. Dereferencing this with *ptr2 would give the first element in p. Dereferencing this again would use the first element in p as an address, and give the value at that address.

This is why you should read the warnings from your compiler. Even without the flags -Wall and -Wextra (which you always should use) this code emits this warning:

k.c:6:16: warning: initialization of ‘int **’ from incompatible pointer type ‘int (*)[3]’ [-Wincompatible-pointer-types]
   int **ptr2 = &p;
                ^

There you have it in plain text. int ** is not compatible with int(*)[3]

Always read compiler warnings.

Comments

0

As you have discovered, you can take the address of an array. the type of that is a generally not very useful "pointer to specific array type", not a generic pointer to pointer to content.

If you must, you can create a pointer that is equivalent to the array, and then take the address of that:

int p[] = {1,2,3};
int *q=p;
int **r=q;

But there is no way to shortcut this step, and you are dependent on the lifetime of that intermediate pointer.

5 Comments

So in the case of doing something like int * p = (int*)malloc(sizeof(int)*3); Then p would be of type pointer to int and in this case int **ptr = &p would work? Its strange because in that case p would act like an array but it wouldn't have that type.
Indeed, yopu can do that as well.
Why do you claim it's not useful to take the address of an array? It might not be appropriate for some particular purpose, but that doesn't mean there is no purpose for it.
@John OK, it was a turn of phrase. In C the type of "pointer to array" isn't generally very useful, compared to a pointer to the elements.
I suspect people who work with (bona fide) multi-dimensional arrays would disagree. Pointers to arrays arise naturally and are very useful in that context. I'd be prepared to accept that applying the address-of operator to an array is rarely what one really wants to do, but that's about array usage, not about the resulting pointer type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.