Разница между const и const volatile

89

Если мы объявляем переменную как volatileкаждый раз, когда обновляется новое значение.
Если мы объявляем переменную как, constто значение этой переменной не будет изменено.

Тогда в const volatile int temp;
чем смысл объявления переменной, tempкак указано выше?
Что произойдет, если мы объявим как const int temp?

user559208
источник
Вы бы не использовали const volatile int temp;в области видимости блока (т.е. внутри { }), там это бесполезно.
MM

Ответы:

145

Объект, помеченный как const volatile, не может быть изменен кодом (из-за constквалификатора будет вызвана ошибка ) - по крайней мере, через это конкретное имя / указатель.

volatileЧасть Классификатора означает , что компилятор не может оптимизировать или изменить порядок доступа к объекту.

Во встроенной системе это обычно используется для доступа к аппаратным регистрам, которые могут быть прочитаны и обновлены оборудованием, но не имеет смысла для записи (или может быть ошибкой при записи).

Примером может служить регистр состояния последовательного порта. Различные биты будут указывать, ожидает ли символ чтения или готов ли регистр передачи принять новый символ (т. Е. - он пуст). Каждое чтение этого регистра состояния может приводить к другому значению в зависимости от того, что еще произошло в оборудовании последовательного порта.

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

Быстрый пример:

unsigned int const volatile *status_reg; // assume these are assigned to point to the 
unsigned char const volatile *recv_reg;  //   correct hardware addresses


#define UART_CHAR_READY 0x00000001

int get_next_char()
{
    while ((*status_reg & UART_CHAR_READY) == 0) {
        // do nothing but spin
    }

    return *recv_reg;
}

Если эти указатели не были помечены как существующие volatile, может возникнуть пара проблем:

  • тест цикла while может считывать регистр состояния только один раз, поскольку компилятор может предположить, что все, на что он указывает, никогда не изменится (в тесте цикла while или самом цикле нет ничего, что могло бы его изменить). Если вы вошли в функцию, когда в оборудовании UART не было ожидающего символа, вы можете попасть в бесконечный цикл, который никогда не прекращался, даже когда символ был получен.
  • чтение регистра приема может быть перемещено компилятором до цикла while - опять же, потому что в функции нет ничего, что указывало бы на то *recv_reg, что цикл изменен, нет причин, по которым его нельзя прочитать перед входом в цикл.

В volatileклассификаторы гарантирует , что эти оптимизации не выполняются компилятором.

Майкл Берр
источник
5
+1 за объяснение. И у меня вопрос: а как насчет методов const volatile? Если у меня есть класс, к которому обращаются многие потоки (хотя доступ синхронизируется с мьютексом), мои константные методы также должны быть изменчивыми (поскольку некоторые переменные могут быть изменены другим потоком)
Sasa
39
  • volatile сообщит компилятору не оптимизировать код, связанный с переменной, обычно, когда мы знаем, что она может быть изменена «извне», например, другим потоком.
  • const сообщит компилятору, что программе запрещено изменять значение переменной.
  • const volatileэто очень особенная вещь, которую вы, вероятно, увидите, использованную ровно 0 раз в вашей жизни (тм). Как и следовало ожидать, это означает, что программа не может изменять значение переменной, но значение может быть изменено извне, поэтому никакая оптимизация не будет выполняться для переменной.
минго
источник
12
Я бы подумал, что volatileпеременные обычно происходят, когда вы начинаете возиться с оборудованием, а не с другими потоками. Я видел, что const volatileиспользуется в таких вещах, как регистры состояния с отображением в память или тому подобное.
ТОЛЬКО МОЕ ПРАВИЛЬНОЕ МНЕНИЕ
2
Конечно, вы совершенно правы, многопоточность - это лишь один пример, но не единственный :).
mingos 04
25
Если вы работаете со встроенными системами, вы увидите это очень часто.
Даниэль Грилло,
28

Это не из-за того, что переменная константа, она не могла измениться между двумя точками последовательности.

Константа - это обещание, которое вы даете не изменять значение, а не то, что значение не будет изменено.

Александр К.
источник
9
Плюс один за указание на то, что constданные не «постоянны».
Богдан Александру
7

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

cantx.id = 0x10<<24 | CANID<<12 | 0;

... путем предварительного вычисления значения константы и использования инструкции немедленной сборки или загрузки константы из ближайшего местоположения, так что любые обновления исходного значения CANID в области конфигурации flash будут игнорироваться. CANID должен быть постоянно изменчивым.

push2eject
источник
7

В C const и volatile являются квалификаторами типа, и они независимы.

По сути, const означает, что значение не может быть изменено программой.

А volatile означает, что значение может внезапно измениться (возможно, извне программы).

Фактически, стандарт C упоминает пример действительного объявления, которое одновременно является константным и изменчивым. Пример такой

«Extern const volatile int real_time_clock;»

где real_time_clock может быть изменен аппаратно, но не может быть назначен, увеличен или уменьшен.

Таким образом, мы уже должны рассматривать const и volatile отдельно. Кроме того, этот квалификатор типа также применяется к struct, union, enum и typedef.

user2903536
источник
5

Вы можете использовать const и volatile вместе. Например, если предполагается, что 0x30 является значением порта, который изменяется только внешними условиями, следующее объявление предотвратит любую возможность случайных побочных эффектов:

const volatile char *port = (const volatile char *)0x30;
Шаг
источник
4

constозначает, что переменная не может быть изменена кодом c, а не то, что она не может быть изменена. Это означает, что никакая инструкция не может записывать в переменную, но ее значение может измениться.

volatileозначает, что переменная может измениться в любое время, и поэтому кэшированные значения не могут использоваться; каждый доступ к переменной должен выполняться по ее адресу памяти.

Поскольку вопрос помечен как «встроенный» и предположим, что tempэто переменная, объявленная пользователем, а не регистр, связанный с оборудованием (поскольку они обычно обрабатываются в отдельном файле .h), примите во внимание:

Встроенный процессор, который имеет как энергозависимую память для чтения-записи данных (RAM), так и энергонезависимую постоянную память данных, например FLASH-память в архитектуре фон-Неймана, где данные и пространство программы совместно используют общую шину данных и адреса.

Если вы объявляете, const tempчто у вас есть значение (по крайней мере, если оно отличается от 0), компилятор присвоит переменной адрес во флэш-пространстве, потому что даже если он был назначен на адрес ОЗУ, ему все равно нужна флэш-память для хранения начального значения. переменной, что делает адрес ОЗУ пустой тратой места, поскольку все операции доступны только для чтения.

В результате:

int temp;- это переменная, хранящаяся в ОЗУ, инициализируемая значением 0 при запуске (cstart), могут использоваться кэшированные значения.

const int temp;- это переменная, хранящаяся в (только для чтения) FLASH, инициализируемая значением 0 во время компиляции, могут использоваться кэшированные значения.

volatile int temp; - это переменная, хранящаяся в ОЗУ, инициализируемая значением 0 при запуске (cstart), кэшированные значения НЕ будут использоваться.

const volatile int temp; - это переменная, хранящаяся в (только для чтения) FLASH, инициализированная 0 во время компиляции, кешированные значения НЕ будут использоваться

А вот и полезная часть:

В настоящее время большинство встроенных процессоров имеют возможность вносить изменения в свою энергонезависимую память только для чтения с помощью специального функционального модуля, который const int tempможет быть изменен во время выполнения, а не напрямую. Иными словами, функция может изменять значение по адресу, где tempоно хранится.

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

Но вот разница:

Если const int tempэто изменяемый идентификатор вместо одноразового программируемого серийного номера и НЕ объявлен volatile, кешированное значение может использоваться до следующей загрузки, то есть новый идентификатор может быть недействительным до следующей перезагрузки или, что еще хуже, некоторых функций может использовать новое значение, в то время как другие могут использовать более старое кешированное значение до перезагрузки. Если const int tempобъявлено IS voltaile, изменение идентификатора вступит в силу немедленно.

Майкл Куш
источник
Ух ты, этот длинный ответ
2

Проще говоря, значение в переменной 'const volatile' не может быть изменено программно, но может быть изменено аппаратно. Неустойчивый здесь предназначен для предотвращения любой оптимизации компилятора.

раджешсам
источник
1

Мы используем ключевое слово const для переменной, когда не хотим, чтобы программа изменяла ее. В то время как, когда мы объявляем переменную const volatile, мы говорим программе не изменять ее, а компилятору, что эта переменная может быть изменена неожиданно из-за ввода из внешнего мира.

Али
источник