Namespaces
Variants

volatile type qualifier

From cppreference.net

Каждый отдельный тип в системе типов C type system имеет несколько квалифицированных версий этого типа, соответствующих одному, двум или всем трём квалификаторам: const , volatile и, для указателей на объектные типы, restrict . На этой странице описываются эффекты квалификатора volatile .

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

Приведение не-volatile значения к volatile типу не имеет эффекта. Для доступа к не-volatile объекту с использованием volatile семантики, его адрес должен быть приведен к указателю-на-volatile, и затем доступ должен осуществляться через этот указатель.

Любая попытка чтения или записи объекта, тип которого квалифицирован как volatile, через не-volatile lvalue приводит к неопределённому поведению:

volatile int n = 1; // объект типа с квалификатором volatile
int* p = (int*)&n;
int val = *p; // неопределённое поведение

Член структуры или объединения с квалификатором volatile приобретает квалификацию типа, к которому он принадлежит (как при доступе с помощью оператора . , так и оператора -> ):

struct s { int i; const int ci; } s;
// тип s.i - int, тип s.ci - const int
volatile struct s vs;
// типы vs.i и vs.ci - volatile int и const volatile int

Если тип массива объявлен с квалификатором volatile (через использование typedef ), тип массива не квалифицируется как volatile, но его тип элементов квалифицируется.

(до C23)

Тип массива и его тип элементов всегда считаются идентично квалифицированными как volatile.

(начиная с C23)
typedef int A[2][3];
volatile A a = {{4, 5, 6}, {7, 8, 9}}; // массив массивов volatile int
int* pi = a[0]; // Ошибка: a[0] имеет тип volatile int*
void *unqual_ptr = a; // OK до C23; ошибка начиная с C23
// Примечание: clang применяет правило из C++/C23 даже в режимах C89-C17

Если тип функции объявлен с квалификатором volatile (с использованием typedef ), поведение не определено.

В объявлении функции ключевое слово volatile может появляться внутри квадратных скобок, используемых для объявления типа массива параметра функции. Оно квалифицирует тип указателя, в который преобразуется тип массива.

Следующие два объявления объявляют одну и ту же функцию:

void f(double x[volatile], const double y[volatile]);
void f(double * volatile x, const double * volatile y);
(since C99)

Указатель на не-volatile тип может быть неявно преобразован в указатель на volatile-квалифицированную версию того же или совместимого типа. Обратное преобразование требует явного приведения типа.

int* p = 0;
volatile int* vp = p; // OK: добавление квалификаторов (int к volatile int)
p = vp; // Ошибка: отбрасывание квалификаторов (volatile int к int)
p = (int*)vp; // OK: приведение типа

Обратите внимание, что указатель на указатель на T не конвертируется в указатель на указатель на volatile T ; для совместимости двух типов их квалификаторы должны быть идентичны:

char *p = 0;
volatile char **vpp = &p; // Ошибка: char* и volatile char* несовместимые типы
char * volatile *pvp = &p; // OK, добавляет квалификаторы (char* к char*volatile)

Содержание

Применение volatile

1) static volatile объекты моделируют порты ввода-вывода с отображением в память, а static const volatile объекты моделируют входные порты с отображением в память, такие как часы реального времени:
volatile short *ttyport = (volatile short*)TTYPORT_ADDR;
for(int i = 0; i < N; ++i)
    *ttyport = a[i]; // *ttyport является lvalue типа volatile short
2) static volatile объекты типа sig_atomic_t используются для взаимодействия с signal обработчиками.
3) volatile переменные, которые являются локальными для функции, содержащей вызов макроса setjmp — это единственные локальные переменные, которые гарантированно сохраняют свои значения после возврата из longjmp .
4) Кроме того, volatile переменные могут использоваться для отключения определенных видов оптимизации, например, для исключения удаления мертвых хранилищ или свертывания констант в микротестах.

Обратите внимание, что volatile-переменные не подходят для взаимодействия между потоками; они не обеспечивают атомарность, синхронизацию или порядок памяти. Чтение 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++ documentation для cv ( const и volatile ) квалификаторов типа