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

В C есть 3 ключевых слова квалификатора типа, из которых в этой статье будет подробно описано, почему и как их использовать с конкретными примерами.

Постоянная

Ключевое слово const используется, чтобы сообщить компилятору, что определенная переменная будет неизменной (она же константа) в течение всего времени существования программы. Это означает, что если вы попытаетесь присвоить значение переменной const, вы, скорее всего, получите ошибку во время компиляции. Сообщение компилятору о том, что переменная является константной, позволяет ему поместить эту переменную в память, доступную только для чтения.

...
const int var = 42; 
var = 21; // This will issue an error. 
...

Использование const с указателями

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

Когда мы используем ключевое слово const перед * при объявлении указателя на int, например:

...
const int *p ; 
...

это означает, что мы объявляем указатель на const int. Сам указатель не является константой, но указывает на постоянное значение int.

Если бы мы, в свою очередь, захотели объявить постоянный указатель на int, это означало бы, что объявленный нами указатель будет указывать на int, которое можно изменить, а адрес самого указателя — нельзя. Мы бы сделали это следующим образом:

...
int * const p;
...

Обратите внимание, что если вы читаете объявления справа налево, заменяя * на «указывает на a», становится легко понять, к чему применяется const:

// pt points to a constant int
const int *pt; 
      
// pt2 points to a constant int (equivalent to pt declaration)
int const *pt1;
// const pt2 (pointer is constant) points to an int
int * const pt2;
// const pt3 (pointer is constant) points to a constant int
const int *const pt3;

Использование const в прототипах функций

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

Например :

void foo(const int x, int y);

Мы можем передать none const int в качестве первого параметра foo, но если мы попытаемся изменить его в функции, компилятор, скорее всего, выдаст ошибку.

Летучий

Использование ключевого слова volatile сообщает компилятору противоположное тому, что сообщает const. Это означает, что он сообщает компилятору, что переменная наверняка будет изменена.

Ключевое слово volatile не позволяет компилятору применять к нему оптимизацию (например, кэширование), тем самым предотвращая любые ненужные накладные расходы. Это особенно полезно во встроенных системах, чтобы реагировать на ограниченные ресурсы или ограничения в реальном времени.

На практике следует объявлять volatile только три типа переменных:

  1. Периферийные регистры с отображением в память

2. Глобальные переменные, измененные подпрограммой обслуживания прерывания

3. Доступ к глобальным переменным для нескольких задач в многопоточном приложении.

Давайте посмотрим на следующий код:

int i = 42;
...
int var1 = i;
/* many operations not involving the use i*/
int var2 = i;
...

В приведенном выше коде i был объявлен как int . Поскольку i не объявлен как volatile, компилятор попытается оптимизировать его (поместив в регистр), поскольку он некоторое время не использовался в блоке, где он определен. Если бы приведенный выше код был в многопоточном контексте, где i изменяется в другом потоке или задаче, эта оптимизация привела бы к ненужным накладным расходам, и изменение могло бы даже не отражаться в var2, что приводило к ошибкам.

используя volatile и const одновременно

В определенных контекстах можно использовать как volatile, так и const для одной и той же переменной.

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

Его объявление будет выглядеть примерно так:

volatile const int loc;

Ограничивать

Квалификатор restrict — это квалификатор типа, предназначенный специально для указателей, чтобы сообщить компилятору, что конкретный указатель является единственной ссылкой на значение, на которое он указывает. Это означает, что на это же значение НЕ ссылается какой-либо другой указатель или переменная в области действия объявления, что делает этот указатель единственным способом доступа к этому объекту данных в памяти. Также важно отметить, что restrict не имеет эквивалента в C++.

Создание указателя restrict позволяет компилятору отказаться от некоторых обычных проверок, которые он должен выполнять без restrict указателей, что в некоторых случаях приводит к значительной оптимизации обработки.

! Нарушение условия указателя restrict приводит к неопределенному поведению.

Синтаксис также немного отличается, ключевое слово restrict ставится после звездочки.

Пример :

...
int * restrict ptr1;
int * restrict ptr2;
...

Приведенный выше код сообщает компилятору, что ptr1 и ptr2 никогда не будут обращаться к одному и тому же значению в течение срока действия области, в которой они определены.

Ключевое слово restrict также может использоваться для параметров функции (которые, конечно же, являются указателями), сообщая компилятору, что никакие другие идентификаторы не изменяют указанные данные в области действия функции.

Пример :

void foo(int * restrict x, int * restrict y, int len)
{
    for(int i = 0; i < len; ++i)
        x[i] += y[i];
}

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