Learn

Arduino pointers, on two lamps and a function that halves a delay

Start building free

Or see every lesson.

  • 455parts on the bench
  • 43boards running now
  • 1.00xreal time, on every board
Arduino Uno: two lamps, and a delay halved through a pointerlive0.000 s 0.00x
Click to open it in the editor
halve(&onMs) gets the address of onMs, so it can change the caller’s variable.

A pointer is a variable that holds the address of another variable. &speed is the address of speed, int *p = &speed; keeps that address in p, and *p goes to the address, so *p = 50; changes speed. On an Uno an address is 2 bytes. Pointers are how a function changes the caller’s variable, and how an array reaches a function at all.

The circuit above is two lamps on an Uno whose blink time is changed through a pointer. Press Run. The serial monitor prints "onMs before = 400" and "onMs after = 200", because halve(&onMs) handed the function the address of onMs and it halved what was there. Then come "sizeof(int*) = 2", "sum of rates = 1500" and two rates read through a pointer, 100 and 400.

Below is & and *, pointers against references, arrays as pointers, walking a string, function pointers and the mistakes, each run on an Uno with what it printed.

Want the step-by-step version? The lesson "Where a thing lives" walks through this with checkpoints.

Open the lesson

The two symbols

In a declaration, * says "pointer to": int *value is a pointer to an int. Everywhere else, *value means "the int at that address", and &onMs means "the address of onMs". Here is the function from the sketch above. It never sees onMs itself, only where it lives, and *value = *value / 2; reads what is there and writes half of it back.

Without the pointer, a function gets a copy of the number and the caller’s variable stays as it was. That is the whole reason to hand over an address.

firmware/uno/examples/cppptr (the function running above)
static void halve(int *value) {
  *value = *value / 2;
}

A function that changes two variables

Swapping two numbers is the classic test. swap(&x, &y) below printed "8 3", so both of the caller’s variables changed. A C++ reference does the same job with less punctuation: int &a in the parameter list, and no & or * anywhere else. swapRef(x, y) swapped them back and printed "3 8".

Arduino libraries use both. A reference cannot be empty and cannot be moved to point somewhere else; a pointer can be nullptr, which a function can test for, and can step through an array.

Swap, with a pointer and with a reference
void swap(int *a, int *b) {
  int keep = *a;
  *a = *b;
  *b = keep;
}

void swapRef(int &a, int &b) {
  int keep = a;
  a = b;
  b = keep;
}

void setup() {
  Serial.begin(9600);
  int x = 3, y = 8;
  swap(&x, &y);
  Serial.print(x);
  Serial.print(" ");
  Serial.println(y);
  swapRef(x, y);
  Serial.print(x);
  Serial.print(" ");
  Serial.println(y);
}

void loop() {
}

An array arrives as a pointer

An array handed to a function is handed over as a pointer to its first element. So in the sketch below, sizeof(rates) printed 8, four ints of 2 bytes, but the same sizeof inside show() printed 2: the size of a pointer. Clang warned: sizeof on array function parameter will return size of 'int *' instead of 'int[]'. That is why a function that takes an array also takes its length, as sumOf(rates, RATE_COUNT) does above.

Indexes and pointers are two spellings of one thing. With int *p = rates;, both *(p + 3) and p[3] printed 800. p++ moves the pointer on by one int, 2 bytes, so *p then printed 200.

sizeof outside and inside a function
int rates[] = {100, 200, 400, 800};

void show(int list[]) {
  Serial.println(sizeof(list));
}

void setup() {
  Serial.begin(9600);
  Serial.println(sizeof(rates));
  show(rates);
}

void loop() {
}

Walking a string, and a list of functions

A string in quotes is a row of characters with a zero byte at the end, and const char *message = "Mokxi"; points at its first letter. A loop that moves the pointer until it reaches the zero printed "M o k x i". That is how strlen() and Serial.print() find the end of your text.

A pointer can hold the address of a function, too. The sketch below keeps three functions in an array and calls each in turn; it printed "slow", "fast" and "off". A menu or a set of light patterns picked by a button press is the usual use: the button changes a number, and modes[number]() runs that mode.

An array of functions
void slow() {
  Serial.println("slow");
}

void fast() {
  Serial.println("fast");
}

void off() {
  Serial.println("off");
}

void (*modes[])() = {slow, fast, off};

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < 3; i++) {
    modes[i]();
  }
}

void loop() {
}

Mistakes, made on purpose

A number where an address goes: int *p = x; stopped the build with cannot initialize a variable of type 'int *' with an lvalue of type 'int'. GCC says invalid conversion from 'int' to 'int*'. Write &x.

An address that has gone away: a function that returns a pointer to its own local array hands back memory that is reused the moment it returns. Clang warned address of stack memory associated with local variable 'text' returned, and the sketch printed a blank line instead of "ready". GCC says address of local variable 'text' returned. Make the array static, or have the caller pass in a buffer to fill.

An address of nothing: a pointer set to nullptr points at nothing. Test if (p == nullptr) before using one that might not have been set; that sketch printed "points at nothing".

Questions

What is a pointer in Arduino?

A variable that holds the memory address of another variable instead of a value. On an Uno it is 2 bytes. *p reads or writes whatever is at that address.

What is the difference between * and & in Arduino code?

& before a variable gives its address. * in a declaration makes a pointer, and * before a pointer anywhere else goes to the value it points at. In a parameter list, & after a type makes a reference instead.

Should I use a pointer or a reference?

Use a reference when a function must change one variable the caller always has. Use a pointer when there might be nothing to point at, or to step through an array.

Why is sizeof wrong inside my function?

The array arrived as a pointer, so sizeof gives the size of the pointer, 2 bytes on an Uno. Pass the length as a second parameter, worked out with sizeof where the array is declared.

Do I need pointers to program an Arduino?

Not to start. You use them without noticing whenever you pass an array or a string to a function, and you need them to write a function that changes the caller’s variables or to keep a list of functions.

Sources

Where the facts on this page come from

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.