Learn

Arduino static variables, global variables and scope

Start building free

Or see every lesson.

  • 455parts on the bench
  • 43boards running now
  • 1.00xreal time, on every board
Arduino Uno: the code bench, printing its variableslive0.000 s 0.00x
Click to open it in the editor
Every variable this sketch prints is a global. Move one into loop() and see what the compiler says.

A variable made inside a function starts again every time the function runs, and disappears when it returns. Put static in front of it and it is made once, keeps its value between calls, and can still only be seen inside that function. A global variable, made outside every function, also keeps its value, but every function in the sketch can see it and change it.

The circuit above is the code bench, whose sketch prints its global variables by name, such as "lampCount = 3", and then shows a number that does not fit its type: "32000 + 1000 in an int = -32536". Globals like these are the usual way to keep a value between passes of loop(); a static is often the tidier one.

Below is static inside a function and inside loop(), the global that never changes, the scope error, and how one global is shared between tabs with extern.

Want the step-by-step version? The lesson "Variables have a size" walks through this with checkpoints.

Open the lesson

static keeps its value between calls

The two functions below are the same but for one word. Called three times each, countLocal() printed "local 1" every time, because its n is made fresh and set to 0 on every call. countStatic() printed "static 1", "static 2" and "static 3": its n was set to 0 once, the first time, and kept counting.

The = 0 on a static runs once. That is the part that surprises people: the line looks as if it resets the count, and it does not.

A local and a static counter
void countLocal() {
  int n = 0;
  n++;
  Serial.print("local ");
  Serial.println(n);
}

void countStatic() {
  static int n = 0;
  n++;
  Serial.print("static ");
  Serial.println(n);
}

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < 3; i++) {
    countLocal();
    countStatic();
  }
}

void loop() {
}

static inside loop()

loop() is a function like any other, so a plain variable in it is lost at the end of every pass. A millis() timer needs its last time to survive, which is why tutorials put it at the top of the sketch. A static in loop() survives too, and keeps the name next to the only code that uses it.

The sketch below printed "tick 1 at 1000", "tick 2 at 2000" and "tick 3 at 3000". With static deleted from both lines, it printed "tick 1 at 1000" and then "tick 1" again on every pass: last is 0 again each time, so after the first second the test is always true, and ticks starts from 0 again too.

A timer that keeps its own last time
void setup() {
  Serial.begin(9600);
}

void loop() {
  static unsigned long last = 0;
  static int ticks = 0;
  if (millis() - last >= 1000) {
    last = millis();
    ticks++;
    Serial.print("tick ");
    Serial.print(ticks);
    Serial.print(" at ");
    Serial.println(last);
  }
}

The global variable that never changes

When a global does not change, look for a second variable with the same name. In the sketch below, int total = 0; inside addReading() makes a new local total that hides the global one. The function adds to the local copy, which is thrown away when it returns, so after three calls the sketch printed "total is 0". Delete the int total = 0; inside the function and it printed "total is 15".

Neither clang nor GCC warns about this by default. The tell is a type name in front of a variable you meant to use rather than make.

A local that hides the global
int total = 0;

void addReading(int value) {
  int total = 0;
  total = total + value;
}

void setup() {
  Serial.begin(9600);
  addReading(5);
  addReading(5);
  addReading(5);
  Serial.print("total is ");
  Serial.println(total);
}

void loop() {
}

Scope: where a name can be seen

A variable made between two braces exists only between them. int ledPin = 13; inside setup() cannot be used in loop(), and the build stopped with use of undeclared identifier 'ledPin'. The Arduino IDE’s GCC says 'ledPin' was not declared in this scope. Move the line above setup(), outside both functions, and both can see it.

The same goes for a variable made inside an if or a for: it is gone after the closing brace. That is useful, because a name used in only one place cannot be changed by accident somewhere else, and it is the reason to prefer a static or a local over a global when only one function needs the value.

One global across tabs: extern

A sketch with a second tab, counter.cpp, needs that tab and the sketch to share one variable. Make it in exactly one .cpp file, and declare it with extern in a header both include. The project below printed "presses = 3".

Two mistakes are common. Making the variable in the header, `int presses = 0;`, makes it once in every file that includes it, and the build stops at the link with duplicate symbol: presses (GCC’s linker says multiple definition of `presses'). Making it `static int presses = 0;` in the header builds, and is worse: each file gets its own private copy, so the counter counted in one and the sketch printed the other, "presses = 0". static on a global means "this file only".

counter.h, counter.cpp and the sketch
// counter.h
#pragma once

extern int presses;
void countPress();

// counter.cpp
#include "counter.h"

int presses = 0;

void countPress() {
  presses++;
}

// sketch.ino
#include "counter.h"

void setup() {
  Serial.begin(9600);
  countPress();
  countPress();
  countPress();
  Serial.print("presses = ");
  Serial.println(presses);
}

void loop() {
}

static, global, volatile and RAM

A variable an interrupt changes needs volatile as well, global or static, so loop() reads it fresh every time instead of keeping an old copy. Statics and globals both live in the Uno’s 2048 bytes of RAM for the whole run, and count toward the "Global variables use" figure the Arduino IDE prints after a build; locals use the stack only while their function runs. A big array that only one function needs, only while it runs, is better local.

Questions

What does static mean on an Arduino variable?

Inside a function, static makes the variable once, keeps its value between calls, and keeps it private to that function. Outside every function, static makes a global private to its own file.

Should I use a static variable or a global variable?

Use a static in the function when only that function needs the value, such as a timer’s last time in loop(). Use a global when several functions, or an interrupt, need to share it.

Why is my Arduino global variable not changing?

Usually a local with the same name hides it: a type name in front, like int total = 0; inside a function, makes a new variable. Remove the type to use the global. If an interrupt changes it, make it volatile too.

Does static int n = 0; reset n to zero each time?

No. The value after the = is set once, the first time the function runs. Each later call carries on from the last value.

How do I use a global variable in another tab?

Make it in one .cpp file, int presses = 0;, and put extern int presses; in a header that every tab using it includes.

Build this for real

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