0

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?

3
  • 1
    Array name decays to a pointer in many (though not all) contexts, but it's not the same as the pointer. You discovered one case where they differ: the type of &array is a pointer to an array, not a pointer to a pointer. Another one is that, in general, sizeof(ptr) != sizeof(array). Commented Sep 26, 2018 at 4:09
  • The warning looks clear to me: what gets passed to ptrptr in the last case is not, as you seem to believe, a uint32_t*, but a uint32_t (*)[1] - a pointer to an array of uint32_t, of length 1. It's also not clear what you mean by "silently" - you are getting a warning, aren't you? Commented Sep 26, 2018 at 4:14
  • 3
    An array is not a pointer, and a pointer is not an array. However, an array when accessed is converted to a pointer to the first element except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array. C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) Commented Sep 26, 2018 at 5:14

2 Answers 2

3

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.

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

Comments

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 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.

Comments

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.