Поделиться через


Квалификаторы типов

Квалификаторы типов предоставляют идентификатору одно из двух свойств. Квалификатор типа 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 (определение стандартной версии языка)
Объявления и типы