Рубрики

Понимание «изменчивый» квалификатор в C | Набор 2 (Примеры)

Ключевое слово volatile предназначено для предотвращения применения компилятором каких-либо оптимизаций к объектам, которые могут изменяться способами, которые не могут быть определены компилятором.

Объекты, объявленные как volatile, не включаются в оптимизацию, поскольку их значения могут быть изменены кодом вне области текущего кода в любое время. Система всегда считывает текущее значение энергозависимого объекта из области памяти, а не сохраняет его значение во временном регистре в той точке, в которой оно запрашивается, даже если предыдущая инструкция запрашивала значение из того же объекта. Таким образом, простой вопрос заключается в том, как значение переменной может измениться так, что компилятор не сможет ее предсказать. Рассмотрим следующие случаи для ответа на этот вопрос.

1) Глобальные переменные, измененные подпрограммой обработки прерываний вне области действия:
например, глобальная переменная может представлять порт данных (обычно это глобальный указатель, называемый IO с отображением в памяти), который будет динамически обновляться. Порт данных для чтения кода должен быть объявлен как энергозависимый, чтобы получать последние данные, доступные в порту. Не объявляя переменную как volatile, компилятор оптимизирует код таким образом, что он будет считывать порт только один раз и будет продолжать использовать это же значение во временном регистре для ускорения программы (оптимизация скорости). Как правило, ISR используется для обновления этих портов данных при наличии прерывания из-за доступности новых данных.

2) Глобальные переменные в многопоточном приложении. Существует несколько способов взаимодействия потоков, а именно: передача сообщений, совместная память, почтовые ящики и т. Д. Глобальная переменная является слабой формой общей памяти. Когда два потока делятся информацией через глобальную переменную, они должны быть квалифицированы как volatile. Поскольку потоки работают асинхронно, любое обновление глобальной переменной из-за одного потока должно быть заново получено другим потоком-потребителем. Компилятор может читать глобальную переменную и помещать их во временную переменную текущего контекста потока. Чтобы свести на нет эффект оптимизации компилятора, такие глобальные переменные следует квалифицировать как volatile

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

Давайте рассмотрим пример, чтобы понять, как компиляторы интерпретируют ключевое слово volatile. Рассмотрим код ниже, мы меняем значение объекта const с помощью указателя и компилируем код без опции оптимизации. Следовательно, компилятор не будет выполнять какую-либо оптимизацию и изменит значение объекта const.

/ * Скомпилировать код без опции оптимизации * /
#include <stdio.h>

int main(void)

{

    const int local = 10;

    int *ptr = (int*) &local;

  

    printf("Initial value of local : %d \n", local);

  

    *ptr = 100;

  

    printf("Modified value of local: %d \n", local);

  

    return 0;

}

Когда мы компилируем код с опцией «–save-temps» в gcc, он генерирует 3 выходных файла

1) предварительно обработанный код (имеющий расширение .i)
2) код сборки (с расширением .s) и
3) объектный код (с опцией .o).

Мы компилируем код без оптимизации, поэтому размер кода сборки будет больше (выделено красным цветом ниже).

Выход:

  [narendra@ubuntu]$ gcc volatile.c -o volatile –save-temps
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 731 2016-11-19 16:19 volatile.s
  [narendra@ubuntu]$

Давайте скомпилируем тот же код с опцией оптимизации (т.е. опцией -O). В приведенном ниже коде «local» объявлен как const (и энергонезависимый), компилятор GCC выполняет оптимизацию и игнорирует инструкции, которые пытаются изменить значение объекта const. Следовательно, значение объекта const остается неизменным.

/ * Скомпилировать код с опцией оптимизации * /
#include <stdio.h>

  

int main(void)

{

    const int local = 10;

    int *ptr = (int*) &local;

  

    printf("Initial value of local : %d \n", local);

  

    *ptr = 100;

  

    printf("Modified value of local: %d \n", local);

  

    return 0;

}

Для приведенного выше кода компилятор выполняет оптимизацию, поэтому размер кода сборки будет уменьшен.

Выход:

  [narendra@ubuntu]$ gcc -O3 volatile.c -o volatile –save-temps
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 10
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 626 2016-11-19 16:21 volatile.s

Давайте объявим объект const как volatile и скомпилируем код с опцией оптимизации. Хотя мы компилируем код с опцией оптимизации, значение объекта const изменится, потому что переменная объявлена как volatile, что означает, что оптимизация не выполняется.

/ * Скомпилировать код с опцией оптимизации * /
#include <stdio.h>

  

int main(void)

{

    const volatile int local = 10;

    int *ptr = (int*) &local;

  

    printf("Initial value of local : %d \n", local);

  

    *ptr = 100;

  

    printf("Modified value of local: %d \n", local);

  

    return 0;

}

Выход:

  [narendra@ubuntu]$ gcc -O3 volatile.c -o volatile –save-temp
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 711 2016-11-19 16:22 volatile.s
  [narendra@ubuntu]$

Приведенный выше пример не может быть хорошим практическим примером, целью которого было объяснить, как компиляторы интерпретируют ключевое слово volatile. В качестве практического примера рассмотрим сенсорный датчик на мобильных телефонах. Водитель, извлекающий сенсорный датчик, будет считывать местоположение касания и отправлять его приложениям более высокого уровня. Сам драйвер не должен изменять (постоянство) место считывания и быть уверенным, что он считывает сенсорный ввод каждый раз при обновлении (изменчивость). Такой драйвер должен считывать входные данные датчика касания постоянным образом.

Примечание. Приведенные выше коды зависят от компилятора и могут работать не на всех компиляторах. Цель примеров — заставить читателей понять концепцию.

Связанная статья:
Понимание «изменчивый» квалификатор в C | Комплект 1 (Введение)

Обратитесь к следующим ссылкам для получения более подробной информации о volatile ключевом слове
Изменчивый: лучший друг программиста
Не используйте volatile в качестве примитива синхронизации

Эта статья составлена Narendra Kangralkar и рецензирована командой GeeksforGeeks. Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.

Рекомендуемые посты:

Понимание «изменчивый» квалификатор в C | Набор 2 (Примеры)

0.00 (0%) 0 votes