Модификаторы

Модификаторы можно разделить на четыре типа: модификаторы времени хранения, класса хранилища, размера и знаковости. Разберемся с ними по порядку.

Модификаторы времени хранения

По умолчанию все создаваемые переменные являются автоматическими — это значит, что область их видимости ограничена блоком, в котором они были объявлены (внутри функции или цикла). Для описания таких переменных имеется специальное слово — auto.

uint32_t a = 0;
auto uint32_t a = 0;

Данные записи идентичны, поэтому писать ключевое слово auto не обязательно. Более того, лучше его не использовать, так как в стандарте C++ данный модификатор обозначает другое.

Следующий модификатор, extern, позволяет создать внешнее связывание переменной, т.е. объявлена она может в одном файле, а использоваться в другом.

// photo_sensor.c
volatile uint32_t adc_data;
// main.c
extern uint32_t adc_data;

Следующий модификатор, static, позволяет объявить статические переменные. Создаются они до входа в main() и инициализируются нулями (если значение не задано).

static uint32_t value;

void main(void) {
    // ...
}

Статическая переменная (или массив) может быть создана и в теле функции. В таком случае доступ к ней будет только внутри самой функции, хотя она и будет хранится в статической памяти. Делать так стоит в тех случаях, когда вы принудительно хотите скрыть переменную, например при использовании таблицы поиска — доступ из какого-либо другого места, кроме функции, которая возвращает предрассчитанное значение, излишен.

float get_temperature(uint8_t adc_value) {
    static const float temp_table[256] = { /* pre-calculated values */ };
    return temp_table[adc_value];
}

Массив temp_table будет создан и проинициализирован один раз.

Последний модификатор register советует компилятору разместить переменную в регистре ядра, чтобы ускорить работу с ней. Он является рудиментарным: современные компиляторы лучше знают, куда и какие переменные помещать. Однако если модификатор всё же используется, то стоит иметь в виду: так как переменная предположительно хранится в регистре ядра, получить ее адрес в памяти невозможно, а значит, невозможно и создать указатель на нее.

Модификаторы класса хранилища

Компилятор — довольно сложная программа, он знает о коде намного больше, чем разработчик, и способен «подправить» программиста, оптимизировав программу. Например, если компилятор не видит, где переменная меняет свое значение, он может для ускорения расчетов закэшировать ее значение, т.е. реальное значение в переменной по ее адресу будет отличаться от значения в кэше. Переменные, которые изменяются асинхронно к самой программе (например, в прерывании) следует создавать с использованием модификатора volatile.

При работе с переменной она подгружается в один из регистров процессора и сохраняется на стеке. При этом ситуация, когда другой участок кода захочет поработать с этой же переменной, не исключена. В таком случае значения в регистре и в ячейке оперативной памяти могут оказаться разными, и данные будут потеряны. Подробнее такая ситуация будет рассмотрена далее.

// photo_sensor.c
volatile uint32_t adc_data;

Если предполагается, что переменная не должна менять свое значение (как иронично), то необходимо применить модификатор const.

const float pi = 3.1415926f;

Иногда, конечно, хочется округлить число Пи до 3 или 4, но теперь этого не позволит сделать компилятор. Этот модификатор может быть использован и в аргументе функции, но об этом позже.

Модификаторы размера

Из практических соображений в язык внесены дополнительные модификаторы для разграничения целых чисел с разным диапазоном значений (зависит от разрядности МК): short для 16-битного int, long для 32-битного и long long для 64-битного.

short int a = 0;
long int  b = 0;
long long c = 0L;

Так как данные модификаторы применимы только к целочисленным переменным, int можно не писать. Для кроссплатформенности лучше явным образом указывать размер переменных.

Модификаторы знаковости

В начале уже упоминалось, что целочисленные переменные могут быть как со знаком, так и без него. Явным образом указывать знак переменой можно через соответствующие модификаторы — signed для знакового и unsigned для беззнакового. Их часто можно встретить в переменных счетчиков, ведь в переменную unsigned помещается число в два раза большее, чем в signed.


Изменено: