cpointersmultidimensional-arrayimplicit-conversiondereference

Difference between pointer to pointer and 2d array


EDIT This is not a question on array decay from char [1] and char *. I know what array decay is for 1D arrays. It however seems different for char [1][1] and char ** since they are not even compatible types.

I know that I can go from one of the types char [1] and char * to the other. However, it seems not as easy with char [1][1] and char **.

In my main function I have:

int main(void) {
    char a[1][1];
    a[0][0] = 'q';
    printf("a: %p\n", a);
    printf("*a: %p\n", *a);
    printf("**a: %p\n", **a);
}

I'm of course compiling with warnings and I know that gcc complains about the 6th line as **a is actually of type char and not a pointer type. However running the code shows that a and *a are actually the same pointer but **a is as expected something else (0x71 which I assume is related to 'q' in some way).

I'm trying to make sense of this and it seems that because *a and a are equal **a must also be equal to a because **a = *(*a) = *(a) = *a = a. It seems that the only error in this reasoning can be the types of a, *a, **a.

How is a actually stored in memory? If a is a pointer to another memory location (in my case 0x7fff9841f250) then surely *a should be the value at that memory address which in my case is also 0x7fff9841f250. So then **a would be the value at 0x7fff9841f250 which is the same value as 'a'... It seems that I cannot view the 2D array char a[1][1] as pointers in a way that makes sense. But then how can I think of this type? What is the type and what does a, *a, **a, a[0], a[0][0] actually mean?

I have already seen Incompatible pointer type but it does not explain what the operations *a, **a, a[0], a[0][0] are actually doing.


Solution

  • a defined as char a[1][1] is an array of 1 array of 1 char: it consists of a single byte of memory set aside by the compiler with automatic storage upon entering the scope of the function main, ie: a single byte of the stack area with an address that happened to be 0x7fff9841f250 when you ran the program, but could be different for a different run or another day, with another compiler, on a different architecture or host...

    This piece of memory uses that same space a single char defined as char a; or as an array of 1 char defined as char a[1]; or as a 3D array defined as char a[1][1][1]; but the type is very different and the type determines how and when the memory is actually read as opposed to its address being taken.

    The address of a is also the address of the first row a[0] and the address of the first element of the first row a[0][0].

    a is the name of the 2D array, when you pass a to a function, it decays to a pointer to its first element, &a[0], which by definition is the address of the array but has type char (*)[1], pointer to arrays of 1 char. Similarly a[0] is an array, so passing a[0] to a function actually passes the address of its first element &a[0][0], which is the same address but with a different type char *.

    The syntax *a is evaluated the same way as a[0] or *(a + 0), (and also more surprisingly *(0 + a), and 0[a]). If a was a pointer, its value would be read when you pass a to printf, but a is an array so a decays as &a[0] and the address is passed without reading the memory. Similarly *a becomes *&a[0] or &a[0][0], ie: *a passed as an argument to printf is just the address of the single element a[0][0]. The compiler generates code that computes the address, such as value of the stack pointer register plus 8.

    Note these remarks:

    To summarize: An array has an address and some contents, whereas a pointer has a value that is an address.

    Trivial comparison: