aprad046
aprad046

Reputation: 666

dereference local array in C

From my understanding of C, you could treat a pointer variable and an array variable as equivalent, since they are both pointers ultimately (one to the local function stack, the other to any random point in memory).

I normally pass a pointer to a pointer (e.g. char ** pvar) when I need to return a pointer, so I can see how it makes little sense to pass it back to a dereferenced local array, since you can't change the position of a variable.

My expectation would be that if I try this, the compiler would let me do it, and then segfault or crash when I try to set the returning pointer value.

However, when trying to dereference an array type (&array), the compiler helpfully generates a warning about using incompatible types, then it passes the pointer to the array, essentially losing one level of indirection from the receiving function's point of view.

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

void ptrptr(uint32_t** dptr)
{
    printf("%x, %x\n",  dptr, *dptr);
}

void oneptr(uint32_t* ptr)
{
    printf("%08x, %x\t",  ptr, *ptr);
    ptrptr(&ptr);
}

int main()
{
    uint32_t array[] = {1};
    uint32_t *ptr = calloc(1, sizeof( uint32_t));
    ptr[0] = 3;
    oneptr(ptr);    /* OK, passes an (uint32_t *)  */
    oneptr(array);  /* OK, passes an (uint32_t *)  */
    ptrptr(&ptr);   /* OK, passes an (uint32_t **) */
    ptrptr(&array); /* ??, passes an (uint32_t *)  */
    return 0;
}

Compiling it gives me the warning

cc     test.c   -o test
test.c: In function ‘main’:
test.c:24:9: warning: passing argument 1 of ‘ptrptr’ from incompatible pointer type [-Wincompatible-pointer-types]
  ptrptr(&array);
     ^
test.c:5:6: note: expected ‘uint32_t ** {aka unsigned int **}’ but argument is of type ‘uint32_t (*)[1] {aka unsigned int (*)[1]}’
 void ptrptr(uint32_t** dptr)
      ^~~~~~
0061a008, 3     7ebfa144, 61a008
7ebfa154, 1     7ebfa144, 7ebfa154
7ebfa150, 61a008
7ebfa154, 1

I get the same result when I use gcc, clang and cl to compile it, so I'm fairly confident that this is not a compiler bug. The question then is, why is C silently passing a pointer ( uint32_t*) instead of a pointer to a pointer (uint32_t**) when I try to dereference an array?

Upvotes: 0

Views: 746

Answers (2)

Lundin
Lundin

Reputation: 213989

The question then is, why is C silently passing a pointer ( uint32_t*) instead of a pointer to a pointer (uint32_t**) when I try to dereference an array?

It isn't silent, it gave you a warning. The C standard does not mention the terms "errors" and "warnings", but speaks of diagnostic messages. To follow the C standard, it is sufficient if the compiler shows a diagnostic message to the programmer.

If you want an error instead of a warning upon C standard violations with gcc or clang, you must compile with -std=c11 -pedantic-errors.

As for why the code isn't correct, &array gives the address of the array in the form of an array pointer, uint32_t(*)[1]. This type is not compatible with uint32_t**. What will happen if you run the program despite it containing a C standard constraint violation isn't specified: it is undefined behavior. There is no guarantee of a seg fault or crash, those are just two of many potential outcomes of undefined behavior.

Upvotes: 0

The question then is, why is C silently passing a pointer (uint32_t*) instead of a pointer to a pointer (uint32_t**) when I try to dereference an array?

  • It isn't.

  • C is passing a pointer to an array of one uint32_t (uint32_t(*)[1]).

  • It's a pointer to an array of one uint32_t, because it was an array of one uint32_t, and you got a pointer to it.

  • It's not silent. You get a compiler warning saying "hey, this is the wrong type of pointer!". What did you think this is?

    test.c: In function ‘main’:
    test.c:24:9: warning: passing argument 1 of ‘ptrptr’ from incompatible pointer type [-Wincompatible-pointer-types]
      ptrptr(&array);
         ^
    test.c:5:6: note: expected ‘uint32_t ** {aka unsigned int **}’ but argument is of type ‘uint32_t (*)[1] {aka unsigned int (*)[1]}’
     void ptrptr(uint32_t** dptr)
    
  • You aren't dereferencing an array. You're making a pointer to an array, converting it to the wrong pointer type, and dereferencing that.

  • The reason you this gives you the number 1 is because a pointer to an array actually points to the same address as a pointer to the first thing in the array. Though, it's a different type of pointer, which means things like ++ work differently, but then you're converting it to the same type of pointer so your code doesn't notice.

Upvotes: 3

Related Questions