Search code examples
cpointersdeclaration

How can * both declare pointers and dereference pointers?


I'm confused as to how the dereference operator functions in pointer declarations.

Consider both these scenarios:

int y = 5;
int *x = &y;
int y = 5;
int *x;
x = &y;

The second scenario (separate declaration and initialization) makes sense to me. After reading through many similar questions, I've noted that declaration resembles usage. That is, *x is an integer, so x is a pointer to an integer. Therefore, the assignment of x to a pointer of y makes sense.

What confuses me is the first scenario (combined declaration and initialization). I struggle to understand why int *x = &y; should work. *x is not a pointer, so why should it be initialized with a pointer?

I've had a few attempts at rationalizing this first scenario. Mainly, I'm trying to determine whether declaration in C just has a special grammar where the value that is assigned with int *x = &y; is x, not *x. Nothing else really makes sense to me.

One explanation that I've seen that I'm not very satisfied with is the higher level "treat int* as a type" explanation. I'm not satisfied with it because it fails for int* x, z.


Solution

  • A unary * always represents a dereference.

    This may feel familiar and intuitive in an expression: *x means to get the value x points to. In contrast, you may find a declaration less intuitive; what does * mean in int *x?

    The way to think of this is that a declaration gives a picture of how something will be used. The declaration int *x says *x will be used as an int. In other words, when we get the thing that x points to, it is an int. The * in a declaration represents the same thing that happens in an expression: dereferencing the pointer.

    Kernighan and Ritchie tell us this in The C Programming Language, 1978, page 90:

    The declaration of the pointer px is new.

    int *px;

    is intended as a mnemonic; it says the combination *px is an int, that is, if px occurs in the context *px, it is equivalent to a variable of the type int. In effect, the syntax of the declaration for a variable mimics the syntax of expressions in which the variable might appear.

    As a more involved example, consider int (*p)[];. This tells us that (*p)[] is an int. Since [] is used to access array elements, this means (*p) must be an array of int. And that means p must be a pointer to an array of of int. Just like *, [] does not have a reversed meaning in declarations. It does not mean “is an array of” instead of “access an element of”; it is still an image of how the thing will be used in an expression.

    In int *x = &y;, the = character does not mean assignment. It means initialization. And what is being initialized is not the expression on the left side of the =, because there is no actual expression on the left side. In the declaration int *x, *x is not an expression; it is only an image of how x would be used in an expression. In int *x = &y;, int *x is declaring x, so it is x that is being initialized, not *x being assigned.