Skip to content
Lesson 9 of 13

Step 1 of 5 · Reading · ~3 min

Learn

Functions and Pointers

Pointers Basics

A pointer is a variable whose value is a memory address. By the end of this lesson you can take an address with &, follow it with *, and write functions that change their caller's variables, which the copy rule from Functions otherwise forbids.

Two operators

int temp_c = 21;
int *where = &temp_c;       /* & = "address of": where holds temp_c's location */

printf("%d\n", *where);     /* * = "value at": follow the address -> 21 */
*where = 30;                /* write THROUGH the pointer */
printf("%d\n", temp_c);     /* 30: same bytes, two names */

Read int *where as "where is a pointer to int". &temp_c produces an address; *where (called dereferencing) goes to that address and reads or writes the int stored there. The two are inverses: *&temp_c is just temp_c.

Draw it as boxes, with made-up addresses:

temp_c  @0x7ffc0010: [ 30         ]
where   @0x7ffc0018: [ 0x7ffc0010 ]  --points to-->  temp_c

where's value is temp_c's location. When a pointer bug confuses you, draw the boxes.

The exception to copy-by-value

A function still receives copies. The trick is to hand it a copy of an address: a copied address points at the same place, so writing through it reaches the caller's variable. This also lets one function produce two results:

void split_minutes(int total, int *hrs, int *mins) {
    *hrs = total / 60;
    *mins = total % 60;
}

int h = 0, m = 0;
split_minutes(135, &h, &m);
printf("%d h %d min\n", h, m);    /* 2 h 15 min */

Trace it: hrs holds the address of h, so *hrs = total / 60 stores 2 into h; *mins stores 15 into m. This is exactly what scanf has been doing with &n since the second lesson: you pass where, it fills in what.

The star is not optional

Inside the function, the parameter name alone is the address and the starred name is the target. Mixing them up compiles and quietly does nothing:

void reset_wrong(int *p) { int zero = 0; p = &zero; }  /* moves the LOCAL pointer */
void reset_right(int *p) { *p = 0; }                   /* changes the caller's int */

int score = 88;
reset_wrong(&score);  printf("%d\n", score);   /* 88 */
reset_right(&score);  printf("%d\n", score);   /* 0 */

Order matters too. Every *p = ... overwrites what was stored there, and the old value is gone unless you copied it somewhere first.

NULL

A pointer that points nowhere holds NULL. Dereferencing it crashes with a segmentation fault, loud and at the guilty line. Initialize every pointer to something real or to NULL, and guard a maybe-NULL pointer with if (p != NULL) (or if (p)) before following it.

Common mistakes

  • Two plain assignments, no saved copy. The first write destroys one value, so input 3 7 prints 7 7: both boxes end up holding the second number.
  • Assigning the pointers instead of the targets (no *). Only the function's local copies of the addresses move, so 3 7 prints 3 7, unchanged.
  • Saving the pointer, not the value. A temporary declared int but filled from a with no star stores an address squeezed into an int; -Wall warns, and the output is a garbage number.
  • Trusting equal inputs. 5 5 passes every broken version above; the hidden tests use distinct, negative and extreme values.

Your exercise

Fill in the body of swap so that the two ints its parameters point to trade places. The starter's main reads the numbers, calls swap(&a, &b) and prints the result, so your whole job is inside the function. The classic trap is overwriting one value before you have kept a copy of it.

Up nextStructsMemory and Structs

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…