Модификаторы
Модификаторы можно разделить на четыре типа: модификаторы времени хранения, класса хранилища, размера и знаковости. Разберемся с ними по порядку.
Модификаторы времени хранения
По умолчанию все создаваемые переменные являются автоматическими — это значит, что область их видимости ограничена блоком, в котором они были объявлены (внутри функции или цикла). Для описания таких переменных имеется специальное слово — 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
.