Квалификатор типа volatile
Каждый отдельный тип в системе типов C тип имеет несколько квалифицированных версий этого типа, соответствующих одному, двум или всем трём const, volatile, и, для указателей на типы объектов, restrict квалификаторам. На этой странице описываются эффекты квалификатора volatile.
Каждый доступ (чтение и запись), осуществлённый через выражение lvalue типа с квалификатором volatile, рассматривается как наблюдаемое побочное действие для целей оптимизации и оценивается строго в соответствии с правилами абстрактной машины (то есть все записи завершаются в некоторый момент времени до следующей точки последовательности). Это означает, что в рамках одного потока выполнения доступ к volatile не может быть исключён из оптимизации или переупорядочен относительно другого наблюдаемого побочного действия, разделённого точкой последовательности точкой последовательности от доступа к volatile.
Преобразование не-volatile значения в тип volatile не оказывает никакого эффекта. Для доступа к не-volatile объекту с использованием семантики volatile, его адрес должен быть преобразован в указатель на volatile, а затем доступ должен быть произведён через этот указатель.
Любая попытка чтения или записи в объект, тип которого имеет квалификатор volatile, через не-volatile lvalue приводит к неопределённому поведению:
volatile int n = 1; // object of volatile-qualified type int* p = (int*)&n; int val = *p; // undefined behavior
Член структуры или объединения с квалификатором volatile приобретает квалификацию типа, к которому он принадлежит (как при использовании оператора . или оператора ->):
struct s { int i; const int ci; } s;
// the type of s.i is int, the type of s.ci is const int
volatile struct s vs;
// the types of vs.i and vs.ci are volatile int and const volatile int| Если тип массива объявлен с квалификатором типа volatile (через использование | (до C23) |
| Тип массива и его элементный тип всегда считаются идентично квалифицированными по volatile. | (с C23) |
typedef int A[2][3];
volatile A a = {{4, 5, 6}, {7, 8, 9}}; // array of array of volatile int
int* pi = a[0]; // Error: a[0] has type volatile int*
void *unqual_ptr = a; // OK until C23; error since C23
// Notes: clang applies the rule in C++/C23 even in C89-C17 modesЕсли тип функции объявлен с квалификатором volatile (через использование typedef), поведение неопределённо.
| В объявлении функции ключевое слово Следующие два объявления описывают одну и ту же функцию: void f(double x[volatile], const double y[volatile]); void f(double * volatile x, const double * volatile y); | (с C99) |
Указатель на не-volatile тип может быть неявно преобразован в указатель на volatile-квалифицированную версию того же или совместимого типа. Обратное преобразование требует выражения преобразования.
int* p = 0; volatile int* vp = p; // OK: adds qualifiers (int to volatile int) p = vp; // Error: discards qualifiers (volatile int to int) p = (int*)vp; // OK: cast
Обратите внимание, что указатель на указатель на T не преобразуется в указатель на указатель на volatile T; для совместимости двух типов их квалификации должны быть идентичными:
char *p = 0; volatile char **vpp = &p; // Error: char* and volatile char* are not compatible types char * volatile *pvp = &p; // OK, adds qualifiers (char* to char*volatile)
Использование volatile
static volatile объекты моделируют порты ввода-вывода с отображением в память, а static const volatile объекты моделируют порты ввода с отображением в память, такие как реальные часы: volatile short *ttyport = (volatile short*)TTYPORT_ADDR;
for(int i = 0; i < N; ++i)
*ttyport = a[i]; // *ttyport is an lvalue of type volatile shortstatic volatile объекты типа sig_atomic_t используются для взаимодействия с обработчиками signal.volatile переменные, локальные для функции, содержащей вызов макроса setjmp, являются единственными локальными переменными, гарантированно сохраняющими свои значения после возврата longjmp.Обратите внимание, что переменные volatile не подходят для межпоточного взаимодействия; они не обеспечивают атомарность, синхронизацию или порядок памяти. Чтение из переменной volatile, которая модифицируется другим потоком без синхронизации или одновременной модификации из двух несинхронизированных потоков, является неопределённым поведением из-за гонки данных.
Ключевые слова
Пример
демонстрирует использование volatile для отключения оптимизаций
#include <stdio.h>
#include <time.h>
int main(void)
{
clock_t t = clock();
double d = 0.0;
for (int n = 0; n < 10000; ++n)
for (int m = 0; m < 10000; ++m)
d += d * n * m; // reads from and writes to a non-volatile
printf("Modified a non-volatile variable 100m times. "
"Time used: %.2f seconds\n",
(double)(clock() - t)/CLOCKS_PER_SEC);
t = clock();
volatile double vd = 0.0;
for (int n = 0; n < 10000; ++n)
for (int m = 0; m < 10000; ++m) {
double prod = vd * n * m; // reads from a volatile
vd += prod; // reads from and writes to a volatile
}
printf("Modified a volatile variable 100m times. "
"Time used: %.2f seconds\n",
(double)(clock() - t)/CLOCKS_PER_SEC);
}Возможный вывод:
Modified a non-volatile variable 100m times. Time used: 0.00 seconds Modified a volatile variable 100m times. Time used: 0.79 seconds
Ссылки
- Стандарт C17 (ISO/IEC 9899:2018):
- 6.7.3 Квалификаторы типов (с. 87-90)
- Стандарт C11 (ISO/IEC 9899:2011):
- 6.7.3 Квалификаторы типов (с. 121-123)
- Стандарт C99 (ISO/IEC 9899:1999):
- 6.7.3 Квалификаторы типов (с. 108-110)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
- 6.5.3 Квалификаторы типов
См. также
Документация C++ для квалификаторов cv типов (const и volatile) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/volatile