static

Квалификатор static заставляет локальную переменную сохранять значение между вызовами функции, оставаясь при этом недоступной снаружи — удобно для счётчиков и фильтров.

Полная статья

Описание

Обычная локальная переменная живёт только внутри одного вызова функции: при входе она создаётся заново, при выходе — уничтожается. Если вам нужно, чтобы переменная «помнила» предыдущее значение, но при этом оставалась недоступной снаружи функции, используйте квалификатор static.

static-переменная инициализируется ровно один раз — при первом вызове функции — и продолжает существовать на протяжении всей работы программы. Последующие вызовы функции работают с тем значением, которое осталось после предыдущего вызова. Область видимости при этом не меняется: переменная по-прежнему доступна только внутри своей функции.

Пример кода

    /* RandomWalk
      Paul Badger 2007
      RandomWalk блуждает вверх и вниз случайным образом между двумя
      граничными значениями. Максимальный шаг за одну итерацию задаётся
      параметром "stepsize".
      Статическая переменная смещается вверх или вниз на случайную величину.
      Эта техника также известна как "розовый шум" и "пьяная походка".
    */

    #define randomWalkLowRange -20
    #define randomWalkHighRange 20
    int stepsize;

    int thisTime;

    void setup() {
      Serial.begin(9600);
    }

    void loop() {
      //  тест функции randomWalk
      stepsize = 5;
      thisTime = randomWalk(stepsize);
      Serial.println(thisTime);
      delay(10);
    }

    int randomWalk(int moveSize) {
      static int place; // переменная для хранения значения в random walk — объявлена static, чтобы сохранять
      // значения между вызовами функции, при этом другие функции не могут её изменить

      place = place + (random(-moveSize, moveSize + 1));

      if (place < randomWalkLowRange) {                               // проверка нижней и верхней границ
        place = randomWalkLowRange + (randomWalkLowRange - place);    // отражаем число обратно в положительном направлении
      }
      else if (place > randomWalkHighRange) {
        place = randomWalkHighRange - (place - randomWalkHighRange);  // отражаем число обратно в отрицательном направлении
      }

      return place;
    }

Где применяется

  • Счётчик вызовов. Удобно считать, сколько раз была вызвана функция, не заводя глобальную переменную.
  • Фильтрация и усреднение. Функция должна помнить предыдущее измерение, чтобы рассчитать скользящее среднее или сгладить шум датчика.
  • Генераторы последовательностей. Например, функция, поочерёдно возвращающая разные значения при каждом вызове.
  • Локальное состояние. Когда логика целиком принадлежит одной функции и незачем «загрязнять» глобальное пространство имён.

Практические замечания

  • static-переменная инициализируется только при первом вызове функции, а не при каждом. Это принципиальное отличие от обычной локальной переменной.
  • Если начальное значение явно не указано, компилятор автоматически инициализирует переменную нулём. Тем не менее лучше задавать его явно — так код становится понятнее.
  • Не злоупотребляйте static: скрытое состояние внутри функции бывает трудно отследить при отладке. Если это состояние важно для нескольких частей программы, честнее объявить отдельную глобальную переменную с понятным именем.
  • Если поведение функции кажется непредсказуемым, проверьте, не накапливает ли static-переменная нежелательное значение из предыдущих вызовов — это частая причина трудноуловимых ошибок.
  • Статические переменные занимают память постоянно, а не только во время работы функции. На платах с ограниченным объёмом ОЗУ (например, Arduino Uno с 2 КБ) это стоит учитывать при проектировании.

static

Полезные материалы