Квалификаторы типов
Квалификаторы типов предоставляют идентификатору одно из двух свойств. Квалификатор типа const
объявляет объект как неизменяемый. Квалификатор типа volatile
объявляет элемент, значение которого можно изменить допустимым образом с помощью средств, недоступных программе, в которой он находится, таких как выполняемый в данный момент поток.
Квалификаторы типов const
, restrict
и volatile
могут использоваться в объявлении только один раз. Квалификаторы типов могут использоваться с любым описателем типа; они не могут находиться после первой запятой в объявлении нескольких элементов. Например, следующие объявления допустимы.
typedef volatile int VI;
const int ci;
Следующие объявления недопустимы.
typedef int *i, volatile *vi;
float f, const cf;
Квалификаторы типов имеют смысл только при обращении к идентификаторам как к l-значениям в выражениях. Дополнительные сведения о левосторонних значениях и выражениях см. в статье Выражения L-Value и R-Value.
Синтаксис
type-qualifier
:
const
restrict
volatile
const
и volatile
.
Ниже представлены допустимые объявления const
и volatile
.
int const *p_ci; // Pointer to constant int
int const (*p_ci); // Pointer to constant int
int *const cp_i; // Constant pointer to int
int (*const cp_i); // Constant pointer to int
int volatile vint; // Volatile integer
Если спецификация типа массива включает квалификаторы типов, определяется элемент, а не тип массива. Если спецификация типа функции включает квалификаторы, поведение не определено. volatile
и const
не влияют на диапазон значений или арифметические свойства объекта.
Ключевое слово
const
можно использовать для изменения любого фундаментального или агрегатного типа, указателя на объект любого типа илиtypedef
. Если элемент объявлен только с квалификатором типаconst
, его тип принимается равным const int. Переменнаяconst
может быть инициализирована или помещена в доступную только для чтения область памяти. Ключевое словоconst
полезно при объявлении указателей на значенияconst
. Так вы сообщите функции, что этот указатель нельзя изменять.Компилятор предполагает, что в любом месте программы к переменной
volatile
может обратиться неизвестный процесс, который использует или изменяет ее значение. Независимо от оптимизаций, указанных в командной строке, нужно создать код для каждого назначения переменнойvolatile
или ссылки на нее, даже если кажется, что он ничего не делает.
Если volatile
используется отдельно, предполагается int
. Описатель типа volatile
можно использовать для предоставления надежного доступа к специальным адресам памяти. Используйте volatile
с объектами данных, к которым можно получить доступ или которые можно изменить с помощью обработчиков сигналов, одновременного выполнения программ или специального оборудования, например регистров управления MMIO. Можно объявить переменную как volatile
на весь срок ее существования или объявить как volatile
только одну ссылку.
- Элемент может одновременно быть
const
иvolatile
, и тогда его невозможно изменить допустимым образом в той же программе, но можно изменить в некотором асинхронном процессе.
restrict
Квалификатор типа restrict
, появившийся в C99 и доступный в режиме /std:c11
или /std:c17
, можно применять к объявлениям указателей. Он определяет сам указатель, а не то, на что он указывает.
restrict
является для компилятора указанием оптимизации и обозначает то, что ни один другой указатель в текущей области не ссылается на то же расположение в памяти. Таким образом, для получения доступа к объекту в течение времени существования такого указателя используется только этот указатель или производное от него значение (например, "указатель + 1"). Это помогает компилятору создавать более оптимизированный код. C++ имеет эквивалентный механизм: __restrict
.
На забывайте, что restrict
считается контрактом между вами и компилятором. Если вы создадите псевдоним для указателя с пометкой restrict
, результат будет неопределенным.
Вот пример с использованием restrict
:
void test(int* restrict first, int* restrict second, int* val)
{
*first += *val;
*second += *val;
}
int main()
{
int i = 1, j = 2, k = 3;
test(&i, &j, &k);
return 0;
}
// Marking union members restrict tells the compiler that
// only z.x or z.y will be accessed in any scope, which allows
// the compiler to optimize access to the members.
union z
{
int* restrict x;
double* restrict y;
};
См. также
/std
(определение стандартной версии языка)
Объявления и типы