Skip to content
VLSI Mentor

C Part I · Module 7 · Pointers

Address-of and Dereferencing

The previous chapter established what a pointer is and then carefully avoided doing anything with one. Addresses were printed and compared, but no example ever read or changed the object an address identified — which is the entire point of having the address in the first place. Two operators close that gap, and they are exact inverses of each other: one takes an object and gives you its address, the other takes an address and gives you back the object. Learning what they do is a five-minute job. Learning to read them without hesitating is the work of this chapter, and most of that work goes into one distinction, because assigning to a pointer and assigning through a pointer look almost identical and do completely different things.

Foundation16 min readCPart IFundamentalsPointers

Part I · Module 7 · Chapter 7.2 · Address-of and Dereferencing

1. The Two Operators

Both are unary — they take one operand, written to their right.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
&object      the address-of operator — yields the address of that object
*address     the indirection operator — yields the object at that address

In words, which is how Chapter 7.1's definition is best held:

& turns an object into an address. * turns an address back into an object.

* is usually called dereferencing, and that is the word you will hear. The standard calls it the indirection operator. Both names describe the same step: following an address to the thing it identifies.

2. Reading Through a Pointer

Azvya Education Pvt. Ltd.VLSI Mentor
reaching the object
#include <stdio.h>

int main(void)
{
    int count = 42;
    int *location = &count;

    printf("count     %d\n", count);
    printf("*location %d\n", *location);

    *location = 99;

    printf("after *location = 99:\n");
    printf("count     %d\n", count);
    printf("*location %d\n", *location);

    return 0;
}

Predict all four numbers before reading on.

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
count     42
*location 42
after *location = 99:
count     99
*location 99

The first pair shows reading. location holds count's address, so *location is count — the same object, reached a different way, giving the same 42.

The second pair shows the thing this module exists for. *location = 99; assigned 99 to count, and the name count does not appear anywhere in that statement. Chapter 5.3 established that a function cannot change a caller's object; what it could not say was that this is the mechanism by which anything reaches an object it does not have a name for. Chapter 7.5 does nothing more than put this line inside a function.

*location is not a copy of count. It is count — an object you can read from and assign to.

3. *p Is an Object, Not Just a Value

That last point deserves to be concrete, because it is what makes writing possible.

Most expressions produce a value and nothing more: a + b gives you a number, and you cannot assign to it. *location is different — it designates an actual object, so it can appear wherever an object is required:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    *location = 99;          /* assign to it */
    *location += 1;          /* compound-assign to it */
    (*location)++;           /* increment it */
    int copy = *location;    /* read from it */

Everything Module 3 said about assignment applies unchanged, because *location is just a way of naming an object.

4. The Distinction That Causes the Trouble

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    p = &b;          /* change WHICH object p points at */
    *p = 20;         /* change the object p points at */

One character apart in appearance. Here they are side by side on the same objects:

Azvya Education Pvt. Ltd.VLSI Mentor
retarget, or modify?
#include <stdio.h>

int main(void)
{
    int a = 1;
    int b = 2;
    int *p = &a;

    printf("start:          a %d  b %d  *p %d\n", a, b, *p);

    *p = 10;
    printf("after *p = 10:  a %d  b %d  *p %d\n", a, b, *p);

    p = &b;
    printf("after p = &b:   a %d  b %d  *p %d\n", a, b, *p);

    *p = 20;
    printf("after *p = 20:  a %d  b %d  *p %d\n", a, b, *p);

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
start:          a 1  b 2  *p 1
after *p = 10:  a 10  b 2  *p 10
after p = &b:   a 10  b 2  *p 2
after *p = 20:  a 10  b 20  *p 20

Read it as state, one operation at a time:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
                      a      b      p points at    *p
start                 1      2      a              1
*p = 10               10     2      a              10     <- the OBJECT changed
p = &b                10     2      b              2      <- the POINTER changed
*p = 20               10     20     b              20     <- a DIFFERENT object changed

The third row is the one to study. Nothing in memory was modified except p itself. a still holds 10 and b still holds 2; the only thing that changed is which object p identifies — and *p consequently reports a different number without anything having been assigned to a or b.

5. The Two Are Inverses

Azvya Education Pvt. Ltd.VLSI Mentor
undoing one with the other
#include <stdio.h>

int main(void)
{
    int x = 7;
    int *p = &x;

    printf("x        %d\n", x);
    printf("*&x      %d\n", *&x);
    printf("*p       %d\n", *p);
    printf("&*p == p ? %d\n", &*p == p);
    printf("*&p == p ? %d\n", *&p == p);

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
x        7
*&x      7
*p       7
&*p == p ? 1
*&p == p ? 1

*&x is x. Take the address of x, then go to the object at that address — you are back where you started.

&*p is p. Go to the object p points at, then take its address — you have p again.

Three stable facts, none of them dependent on what the addresses actually are. This is the cleanest statement of the relationship and it needs no analogy:

& and * undo each other. Applying both, in either order, gives you back what you started with.

6. The * in a Declaration Is Not This Operator

Chapter 7.1 §3 flagged this, and it is worth repeating now that both uses appear in the same programs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    int *p = &x;        /* declaration: * is part of the type "pointer to int" */
    *p = 99;            /* expression: * is the indirection operator */

The same character, two unrelated jobs, decided by where it appears. A declaration says what type a name has; an expression computes something. There is a historical reason the same symbol is used — the declaration is written to resemble the expression that would yield an int — but treating them as one thing causes confusion that treating them as two does not.

A practical consequence: int *p = &x; initialises p, not *p. It is one declaration with an initialiser, not an assignment through a pointer that does not exist yet.

7. Precedence

* is a unary operator, so it binds tighter than the binary arithmetic operators — Chapter 3.10's table applies unchanged.

Azvya Education Pvt. Ltd.VLSI Mentor
where the parentheses matter
#include <stdio.h>

int main(void)
{
    int n = 5;
    int *p = &n;

    printf("*p + 1   %d   (read, then add)\n", *p + 1);
    printf("n        %d\n", n);

    (*p)++;
    printf("after (*p)++:  n %d\n", n);

    ++(*p);
    printf("after ++(*p):  n %d\n", n);

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
*p + 1   6   (read, then add)
n        5
after (*p)++:  n 6
after ++(*p):  n 7

*p + 1 groups as (*p) + 1 — dereference, then add — and does not modify n, which is why the second line still prints 5.

(*p)++ needs its parentheses. They say increment the object p points at, and n goes from 5 to 6. Without them, *p++ means something different and involves a rule this module has not reached yet; Chapter 7.8 covers it, and until then, parenthesise.

8. A Realistic Use

A pointer that is reassigned is a way of selecting which object an operation applies to:

Azvya Education Pvt. Ltd.VLSI Mentor
one update, two possible targets
#include <stdio.h>

int main(void)
{
    int accepted = 0;
    int rejected = 0;

    int verdicts[6] = {1, 0, 1, 1, 0, 1};
    size_t count = sizeof verdicts / sizeof verdicts[0];

    for (size_t i = 0; i < count; i++) {
        int *tally = verdicts[i] ? &accepted : &rejected;
        (*tally)++;
    }

    printf("accepted %d, rejected %d\n", accepted, rejected);

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
accepted 4, rejected 2

The increment is written once. Which counter it lands on is decided by a pointer, chosen per pass. The alternative — an if with two nearly identical increments — duplicates the operation, and duplicated operations drift apart when one of them later grows a second line.

Three details are worth naming. tally is declared inside the loop body, so it is a fresh object each pass (Chapter 5.6). The conditional operator from Chapter 4.2 produces a value, and &accepted and &rejected are both int *, so the branches agree in type exactly as Chapter 4.2 §3 requires. And (*tally)++ carries its parentheses for §7's reason.

9. The Question This Raises

Every * in this chapter was applied to a pointer that had just been initialised from &something, so it certainly identified a real object.

Nothing in C requires that. A pointer is an object, and Chapter 2.3 established that an object you never assigned to holds an indeterminate value — which for a pointer means it does not identify anything in particular. Dereferencing such a pointer is not a check that fails; it is the kind of bug Chapter 6.4 classified, with the same absence of guarantees.

That is Chapter 7.4's subject, together with the value C provides for saying this pointer deliberately points at nothing.

10. What You Should Be Able to Say Now

  1. & turns an object into an address; * turns an address back into an object.
  2. * is dereferencing, also called indirection.
  3. Reading through a pointer gives the object's value; *location and count are the same object.
  4. Writing through a pointer changes the original — *location = 99 sets count to 99 with no mention of count.
  5. *p designates an object, so it can take assignment, compound assignment and increment.
  6. p = X changes which object p points at. *p = X changes the object it points at.
  7. Retargeting modifies only the pointer — no pointed-to value changes.
  8. & and * are inverses: *&x is x, and &*p is p.
  9. The * in a declaration is part of the type, not the operator.
  10. *p + 1 adds after dereferencing; (*p)++ needs its parentheses, and *p++ waits for Chapter 7.8.

11. Exercises

Exercise 1 — predict the output. Do not compile yet.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int n = 3;
int *p = &n;
printf("%d %d\n", n, *p);
*p = 8;
printf("%d %d\n", n, *p);
n = 1;
printf("%d %d\n", n, *p);

The third pair is the interesting one — explain why both numbers change when only n was assigned.

Exercise 2 — trace the state. Write the table, in §4's style, for this sequence, giving x, y, what q points at, and *q after each statement.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int x = 1, y = 2;
int *q = &y;
*q = 5;
q = &x;
*q = 9;
*q = *q + 1;

Exercise 3 — classify each statement. For each, say whether it changes the pointer, changes a pointed-to object, or changes neither.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p = &a;
*p = 4;
p = p;
*p = *p;
int z = *p;

Exercise 4 — apply the inverses. Give the value of each, assuming int m = 6; int *r = &m;, and say which are guaranteed regardless of the actual addresses.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
*&m        &*r == r        *r         *&r == r        **&r

Exercise 5 — find the bug. This is meant to put 50 into value. Say exactly what it prints, which of §4's two forms the author needed, and give the repair.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int value = 10;
int fifty = 50;
int *p = &value;
p = &fifty;
printf("%d\n", value);

Exercise 6 — add the parentheses. For int k = 4; int *pk = &k;, give the value of k afterwards and say what each expression does.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
(*pk)++;
++(*pk);
*pk += 3;
*pk = *pk * 2;

Exercise 7 — explain the selector. In §8, tally is reassigned on every pass and (*tally)++ is written once. Explain what would have to change if the body grew to update two counters per verdict, and say which version — the pointer or an if with duplicated code — you would rather extend.

Exercise 8 — describe it in words. Without using the words pointer, address or memory, write one sentence for & and one for * that would let a colleague predict what *&x evaluates to. Then say what your sentences predict for &*p.

12. Summary

  • & yields an address from an object; * yields the object at an address.
  • * is dereferencing, and it works both for reading and for writing.
  • Writing through a pointer changes the original object — the mechanism Chapter 5.3 said was missing.
  • *p designates an object, so every assignment form from Module 3 applies to it.
  • p = X retargets; *p = X modifies. Retargeting changes nothing but the pointer.
  • The two operators are exact inverses, proven by &*p == p and *&p == p.
  • The * in a declaration is part of the type; the * in an expression is the operator.
  • (*p)++ needs parentheses; *p++ is Chapter 7.8's.

Both operators have been applied to one pointer type throughout, and the type did no visible work: int * was declared, & produced a matching address, and * read four bytes back. That uniformity hides a question worth asking, because Chapter 7.1 showed that a char *, an int * and a double * all occupy the same eight bytes and all hold the same kind of value. If the representation is identical, it is not obvious why C insists on keeping them as separate types rather than providing one pointer that holds any address — and the answer turns out to be the thing that makes * mean anything at all.

Standards & specifications

Governing standard
ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)

Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C Programming curriculum.