Skip to content
VLSI Mentor

C Part I · Module 7 · Pointers

What a Pointer Actually Represents

Pointers have a reputation for being the point at which C becomes difficult, and almost none of that reputation is earned by the idea itself. Every piece of it is already in your hands. Module 2 established that an object is storage at an address. Module 5 established that a call copies its arguments, so a function cannot reach a caller's object. Module 6 established that an array is a run of storage with no stored length, and that a function cannot be handed one yet. Each of those left something unfinished, and all three are finished by the same small addition: C has a type whose values are addresses. This chapter introduces nothing beyond that sentence. It spends its length making the sentence concrete, because the usual difficulty with pointers comes from being given an analogy instead of being shown the thing.

Foundation17 min readCPart IFundamentalsPointers

Part I · Module 7 · Chapter 7.1 · What a Pointer Actually Represents

1. The Address, at Last

Chapter 2.2 established that every object occupies storage beginning at an address, and then deliberately declined to show you one. Five modules have used that picture — sizeof, the call stack, array layout, bounds — without a single address appearing on screen. Here it is:

Azvya Education Pvt. Ltd.VLSI Mentor
where does an object live?
#include <stdio.h>

int main(void)
{
    int count = 42;

    printf("count holds        %d\n", count);
    printf("count lives at     %p\n", (void *)&count);
    printf("sizeof count       %zu bytes\n", sizeof count);

    return 0;
}

Output from one run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
count holds        42
count lives at     0x16dcd2778
sizeof count       4 bytes

Three new pieces of notation appear, and two of them are temporary scaffolding:

&count is the address-of operator. Applied to an object, it produces that object's address. Chapter 7.2 covers it properly; for now, take it as the operator that gives you where something is.

%p is printf's conversion for printing an address.

(void *) is a cast, required because %p expects a particular pointer type. Chapter 7.12 explains what void * is; until then, treat (void *) before a %p argument as part of the printing recipe.

2. A Type Whose Values Are Addresses

An address is a value. Chapter 2.1 established that values live in objects, and that an object's type decides what its bits mean. So the obvious question is whether C has a type for this kind of value.

It does:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    int *location;

That declares an object named location whose values are addresses of int objects. Read the declaration as: location is a pointer to int.

Once you have the type, nothing else is new — it behaves like any other object:

Azvya Education Pvt. Ltd.VLSI Mentor
an object holding an address
#include <stdio.h>

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

    printf("count holds              %d\n", count);
    printf("count lives at           %p\n", (void *)&count);
    printf("location holds           %p\n", (void *)location);
    printf("location itself lives at %p\n", (void *)&location);
    printf("sizeof count             %zu bytes\n", sizeof count);
    printf("sizeof location          %zu bytes\n", sizeof location);

    return 0;
}

Output from one run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
count holds              42
count lives at           0x16ba52778
location holds           0x16ba52778
location itself lives at 0x16ba52770
sizeof count             4 bytes
sizeof location          8 bytes

Four lines of that output carry the whole definition, so read them one at a time.

Lines two and three are the same number. count lives at 0x16ba52778, and location holds 0x16ba52778. That is the definition, observable rather than asserted:

A pointer is an object whose value is the address of another object.

Line four is a different number. location itself lives somewhere — 0x16ba52770 — because location is an object too. It is declared, it occupies storage, it has an address, and you could take its address with &location, as that line does. A pointer is not a special annotation attached to count; it is an ordinary object that happens to hold a particular kind of value.

Lines five and six disagree. count is 4 bytes and location is 8. A pointer's size is a property of the pointer, not of what it points at — which makes sense once you see that what it holds is an address, and an address is the same kind of thing regardless of what is stored there.

Two names refer to two separate objects. The name count refers to an int object at address 1000, which holds the value 42. The name location refers to a different object, of type pointer to int, at address 2000, and that object holds the value 1000. An arrow runs from the pointer object up to the int object, labelled points at, showing that the value 1000 identifies where count begins.counta name in mainint object at 1000holds 42locationa name in mainint * object at 2000holds 1000refers torefers topoints at12
Figure 1 — what the output in §2 describes. Two names, two objects. The object count holds the value 42. The object location is a separate object of type pointer-to-int, and the value it holds is the address at which count begins. The upward arrow is not a third thing in memory; it is the reading of location's value as an address. The addresses shown are arbitrary labels chosen to make the picture readable — real ones differ on every run.

The arrow is worth one sentence of care. There is no arrow in memory. There are two objects, and one of them holds a number that happens to be where the other one starts. Drawing it as an arrow is a reading aid, and it is a good one — but when you need to reason precisely, go back to this object holds that address.

3. Reading the Declaration

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    int *location;

The * in a declaration is part of the type being declared. It is not the dereference operator, which Chapter 7.2 introduces and which appears in expressions rather than declarations. Beginners meet the same character doing two jobs in their first hour with pointers, and keeping them apart from the start removes most of the confusion.

The type of location is int *, pronounced pointer to int. The compiler reads the declaration as *location would be an int, which is a historical way of writing it that explains the placement of the *.

Two spacings are common and identical to the compiler:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    int *location;        /* the * grouped with the name */
    int* location;        /* the * grouped with the type */

The first is what this curriculum uses, for a reason that is only visible when you declare two at once:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    int *a, *b;           /* two pointers, which is probably what you wanted */
    int* a, b;            /* a is a pointer; b is a plain int */

The * attaches to the declarator, not to the type, so int* a, b; declares one pointer and one int — almost never what the author meant. Writing int *a keeps that fact visible. Better still, declare one object per line.

4. Several Pointers, One Object

Nothing restricts how many pointers hold the same address:

Azvya Education Pvt. Ltd.VLSI Mentor
two pointers, one target
#include <stdio.h>

int main(void)
{
    int shared = 7;
    int *first = &shared;
    int *second = &shared;

    printf("first  holds %p\n", (void *)first);
    printf("second holds %p\n", (void *)second);
    printf("first == second ? %d\n", first == second);
    printf("first == &shared ? %d\n", first == &shared);

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
first  holds 0x16f786738
second holds 0x16f786738
first == second ? 1
first == &shared ? 1

The addresses vary per run; the two comparisons are 1 every time, and those are the lines to take away. Pointers can be compared with == and != exactly like other values, and two pointers compare equal when they hold the same address — that is, when they point at the same object.

This is also the first hint of why pointers are useful. shared is one object, and now three expressions refer to it: the name shared, and the values held by first and second. A function given one of those values would be able to reach shared without having a name for it, which is Chapter 7.5's subject and the answer to a limitation Module 5 could only describe.

5. The Size of a Pointer

Azvya Education Pvt. Ltd.VLSI Mentor
three pointer types
#include <stdio.h>

int main(void)
{
    char   c = 'x';
    int    i = 1;
    double d = 2.0;

    char   *pc = &c;
    int    *pi = &i;
    double *pd = &d;

    printf("sizeof (char)    %zu   sizeof (char *)   %zu\n", sizeof (char), sizeof pc);
    printf("sizeof (int)     %zu   sizeof (int *)    %zu\n", sizeof (int), sizeof pi);
    printf("sizeof (double)  %zu   sizeof (double *) %zu\n", sizeof (double), sizeof pd);

    return 0;
}

Output on a typical 64-bit desktop implementation:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sizeof (char)    1   sizeof (char *)   8
sizeof (int)     4   sizeof (int *)    8
sizeof (double)  8   sizeof (double *) 8

The element types are 1, 4 and 8 bytes. All three pointers are 8.

A pointer's size has nothing to do with the size of what it points at. It is whatever that implementation needs to represent an address.

Be careful about the second observation, though — that all three pointers are the same size.

6. What a Pointer Is Not

Three beliefs are worth correcting immediately, because each one makes a later chapter harder.

A pointer is not the object it points at. location and count are two objects holding two different values — 42 and an address. Assigning to one does not touch the other. Chapter 7.2's dereference operator is what lets you cross from one to the other, and it is a deliberate step rather than something that happens by itself.

A pointer is not a name. Names are resolved by the compiler, as Chapter 5.6 established, and they do not exist while the program runs. A pointer is a value, computed and stored and passed around at run time, which is exactly why it can do things a name cannot.

A pointer is not inherently dangerous. It is a value of a particular type. What is dangerous is using one that does not currently identify a valid object — and that specific hazard is the subject of Chapter 7.4 and Module 9, not a property of pointers as such.

7. Why This Was Worth Waiting For

It is worth seeing what this one type unlocks, because all three debts come due in the next few chapters.

Left unfinishedByFinished in
A function cannot change a caller's object5.37.5 — pass its address
An array passed to a function loses its length5.3, 6.17.6, 7.7 — arrays convert to pointers
You cannot refer to an object computed at run time6.1this chapter — a pointer is such a reference

Module 5 could state its limitation precisely and could not repair it. Module 6 had to keep every array inside main. Both were waiting for a value that identifies an object, and that is all a pointer is.

8. What You Should Be Able to Say Now

  1. An address is a value — Chapter 2.2's idea, now printable with %p.
  2. Addresses differ on every run; only the relationships between them are reproducible.
  3. &object yields that object's address. Chapter 7.2 covers it properly.
  4. int *location; declares an object whose values are addresses of int objects.
  5. A pointer is an object whose value is the address of another object — shown by two printed lines matching.
  6. A pointer is itself an object: it has its own address, which you can take.
  7. A pointer's size is unrelated to the size of what it points at.
  8. All pointers being the same size is implementation behaviour, not a C guarantee.
  9. The * in a declaration is part of the type; int *a, *b; declares two pointers and int* a, b; does not.
  10. Pointers compare with ==; two pointers are equal when they identify the same object.

9. Exercises

Exercise 1 — read the declarations. For each, say what is declared and what type each name has.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int *p;
double *q;
char *r, *s;
char* t, u;
int v, *w;

Exercise 2 — predict what matches. Without compiling, say which of these print the same number as each other, and which vary between runs.

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

Exercise 3 — predict the comparisons. Give the value of each, and say which are reproducible.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int a = 1, b = 1;
int *pa = &a, *pb = &b, *pa2 = &a;
pa == pa2
pa == pb
a == b

Explain why the second and third differ even though a and b hold the same value.

Exercise 4 — size reasoning. On an implementation where int is 4 bytes, double is 8 and pointers are 8, give sizeof for each, and say which answers are guaranteed by C and which are implementation-defined.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int x;      int *px;      double y;      double *py;

Exercise 5 — correct the claim. A colleague says "a double * must be bigger than a char *, because a double is bigger than a char." Say what is wrong with the reasoning, what the actual rule is, and what you would write if you genuinely needed the size of a double *.

Exercise 6 — name the two objects. For int count = 42; int *location = &count;, list the objects that exist, give each one's type and value, and say which one &location is the address of.

Exercise 7 — explain the arrow. Figure 1 draws an arrow from the pointer object to the int object. Explain in two sentences what is actually stored, and why the arrow is a reading aid rather than a description of memory.

Exercise 8 — connect it forward. Chapter 5.3 showed that swap exchanged two objects that were never the caller's. Using only what this chapter establishes, explain what you would have to give swap in order for it to reach the caller's objects — without yet saying how it would use what it was given.

10. Summary

  • An address is an ordinary value; %p prints one, and &object produces one.
  • Addresses vary between runs — only equalities and differences are reproducible.
  • A pointer is an object whose value is the address of another object. Nothing more.
  • It is its own object, with its own address and its own size.
  • A pointer's size is unrelated to its target's, and equal pointer sizes are implementation behaviour.
  • In a declaration the * belongs to the declarator: int *a, *b;.
  • Pointers compare with ==; equal means same object.
  • This one type finishes what Modules 5 and 6 had to leave open.

The definition is now in place, and one thing is conspicuously missing from it: having an address is not the same as being able to use the object it identifies. Every example in this chapter printed addresses or compared them, and not one of them read or changed the value 42 through location. That crossing — from an address to the object at that address — is a single operator, it is the exact inverse of the one that produced the address, and the next chapter is about using both of them without having to think.

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.