2

* declares a pointer. I usually think "int pointer a" when writing int *a.

But then it gets confusing:

int b = *a; stands for "int b is value of a", not "pointer of a". And with int *a = &b the confusion then is perfect, as the new symbol & now stands for "pointer of b" again.

Does anyone know the secret logic behind this convention? Why doesn't * simply stand for "pointer" and & for "value"? Or can these two symbols be thought as other words, so that it makes more sense?


EDIT: How this question is different from the four following questions that have been linked as possible duplicates, highlighting the difference between the associated questions and mine

My question asks about

  1. the intended logic behind the two symbols * and &
  2. how the * and & symbols can be thought / spoken in words

the linked ones don't. In addition to that, neither of the answers to these questions mention the page 90 of the 1978 K&R, where it sates that the declaration of the pointer is intended as a mnemonic. This is exactly what I was looking for.

doej1367
  • 311
  • 2
  • 12
  • 2
    Shoot. Where's my [copy of K&R](https://en.wikipedia.org/wiki/The_C_Programming_Language)? They might cover this decision. – user4581301 May 01 '23 at 21:45
  • 1
    In an expression `x = *p`, `x` gets the value pointed to by `p`. So think of `*` as the "contents of" operator. And then in `int *p;`, the way to read it is, "the contents of `p`, that is, `*p`, will be an `int`. That's why the form `int *p` is preferred over `int* p`. (And then `&` is the "address of" operator, or "construct a pointer to".) – Steve Summit May 01 '23 at 21:45
  • I think it's better to see `*` and `&` as operators, they do multiple functions depending on the situation. For example, `&` acts as a reference but also as a pointer-getter, and `*` acts as a value-from-pointer-getter and pointer-storage. – vitopigno May 01 '23 at 21:48
  • 1
    Just to add to your confusion: Asking for c++ in addition to c adds even more semantics to thd `&` symbol. I'd recommend you get one of those beginner tutorials or books mwhich explain all these meanings in different context in detail. – πάντα ῥεῖ May 01 '23 at 21:50
  • There is a difference between _declaring_ a pointer and _dereferencing_. – Neil May 01 '23 at 21:54
  • 4
    Declarations should be read inside out. Obviously `int a;` means `a` has type `int`, but `int *a;` means `*a` has type `int` (in other words `a` is a pointer to `int`). So the use of `*` in an expression and in a declaration is perfectly consistent. Things aren't so simple with `&`. – john May 01 '23 at 21:54
  • Useful ref: `int *a` --> [`a` as pointer to int](https://cdecl.org/?q=int+*a). – chux - Reinstate Monica May 01 '23 at 22:09
  • Related: [How to explain C pointers (declaration vs. unary operators) to a beginner?](https://stackoverflow.com/q/27484168/2402272) – John Bollinger May 01 '23 at 23:43

3 Answers3

7

The logic is this:

  • Unary * always indicates dereferencing of a pointer.
  • When an expression is evaluated, the unary * causes the pointer to be dereferenced.
  • A declaration provides a “picture” of how an identifier will be used in expressions.1 So unary * in a declaration tells us that dereferencing the identifier produces the declared type. (See more below.)

In contrast, the = has different meanings in a declaration and an expression:

  • In a declaration, the = after a declarator introduces initialization for the object being declared. It applies to the specific object being declared, not to the whole “expression” that is pictured.
  • In an expression, the = means to assign a new value to the object on the left.

So int *a; means “When *a appears in an expression, it is an int.” Therefore, it tells us that the type of a is a pointer to an int.

Some additional discussion is here and here.

int b = *a; stands for "int b is value of a", not "pointer of a".

int b = *a; says “b is an int, and initialize b with the value of *a.”

Note that *a is not the value of a. It is what a points to.

And with int *a = &b the confusion then is perfect, as the new symbol & now stands for "pointer of b" again.

int *a = &b says “*a is an int, so the type of a is a pointer to an int, and initialize a with the address of b.”

This logic also helps understand declarations of multiple items. Consider int a, *b, c[3];. This says “a, *b, and c[i] are each an int.” From this, we conclude type type of a is an int, the type of b is a pointer to an int, and the type of c is an array of int. (The “picture” logic is stretched by arrays, because the 3 in c[3] tells us the size of the array instead of showing an example use of c. So the analogy is not perfect.)

(This is also why writing int *a fits the C and C++ grammars more closely than int* a does; the formal grammars bind * with a before binding with int.)

Footnote

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

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    "So `int *a;` means *When `*a` appears in an expression, it is an `int`.* Therefore, it tells us that `a` is a pointer to an `int`." is a fallacious argument, at least in C++. "`*a` is an `int`" does NOT imply that `a` is an `int*`. `int *a;` specifically means that `a` has type `int*`, and not merely "`*a` is an `int`" and logical consequences of that statement. – Ben Voigt May 01 '23 at 22:12
  • (Yes, it's OP's fault for asking about two languages in one question, but if an answer only covers one it needs to clearly say that) – Ben Voigt May 01 '23 at 22:15
  • @BenVoigt Is your argument, then, that in C++, "`*a` is an `int`" merely implies that "`a` is of a type such that `operator*` yields an `int`"? – Steve Summit May 02 '23 at 01:59
  • @SteveSummit That's correct. That's true for a large number of types (and is a pretty useful template parameter constraint aka "concept") – Ben Voigt May 02 '23 at 04:20
  • @BenVoigt: The explanation is about the logic of declarations, which was created when C was created and was later inherited by C++. While the current existence of overloaded operators would preclude concluding that “The type of `*x` is `int`” implies “The type of `x` is pointer to `int`,” that was not true when the declaration grammar was created and therefore has no bearing on the logic involved in the original design. – Eric Postpischil May 03 '23 at 23:35
4

I usually think "int pointer a" when writing int *a.

The right way to think of it — bear with me for a moment — is that when you write

int *ip;

you are saying that *ip will be an int. From this it can be deduced that ip is a pointer to an int.

This is the "declaration mimics use" strategy. In the declaration

int *ip;

we see that *ip will be an int, and in an expression later when we do something like

*ip = 42;

or

printf("%d\n", *ip);

we can see that *ip is acting like an int — the form *ip in the declaration mimics the form *ip when we later use it.

An important point is that when you declare variables in C, the syntax is not

type-name list-of-variables;    /* WRONG */

The actual syntax is

type-name list-of-declarators;

On the left there's the name of a type, and on the right there's a list of things that will have that type — but the "things" are not necessarily simple variable names! In fact, there's a whole 'nother little programming language you can use while constructing the declarators. As a mildly extreme example, if you say

int i, j, a[10], *ip, f();

you are saying that i, j, a[], *ip, and f() will all have type int — which is another way of saying that i and j will be plain int variables, a will be an array of int, ip will be a pointer to int, and f will be a function returning int.

Now, going back to what you had said:

int b = *a; stands for "int b is value of a", not "pointer of a"

If you think of * as meaning "contents of", the confusion should largely go away. If you think of * as meaning "contents of", then the declaration

int *a;

is read as "the contents of a will be an int", and the initialization

int b = *a;

is read as "b gets the contents of a".

(But I'm not saying you have to think of it that way all the time. When I see int *a, yes, I read it as "a is a pointer to an int". The explanation here is of how the notation came about. It can be confusing, but it's much less confusing if you understand where it came from and how it works.)

And with int *a = &b the confusion then is perfect, as the new symbol & now stands for "pointer of b" again.

& doesn't just mean "pointer", it means "make a pointer to" or "take the address of".

But the form

int *a = &b;

is definitely confusing, because you are not providing an initial value for *a — you are in fact providing an initial value for the pointer a itself. If you do it on two lines, you have

int *a;
a = &b;

You do not say

*a = &b;      /* WRONG */

When you're working with pointers in C, you always have to be careful to distinguish between the pointer and what the pointer points to. When you say

a = &b;

you're making an assignment to the pointer a. When you say

*a = 42;

you're making an assignment to what the pointer a points to.

And when you say

int *a = &b;

you are simultaneously declaring the pointer, and providing an initial value for the pointer. (In that declaration you're not doing anything with what the pointer points to.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
0

The * symbol is used in two ways regarding pointers. The first is in a declaration such as the following:

int *a;

This declares a as a pointer to an int. Another way of looking at it is that *a is declared as an int. The latter makes more sense when multiple identifiers are declared:

int *a, b, *c;

Here, each of *a, b, and *c have type int.

The second use of * regarding pointers is in an expression. In this case, * is the dereference operator. It takes a pointer as its argument, and the resulting expression is what that pointer points to.

The above is related to & which is the address-of operator. It takes an lvalue, i.e. a name or expression designating an object, and the result is the address of that object.

So when you see something like *a, you know that a is a pointer.

dbush
  • 205,898
  • 23
  • 218
  • 273