Указатель как постоянный, так и изменчивый

Во время чтения я наткнулся на этот тип объявления и следующую строку -

const volatile char *p=(const volatile char *) 0x30;

Значение p изменяется только внешними условиями

Я не понимаю, какие внешние условия. А также что такое практическое использование этого типа декларации?

Ответ 1

const говорит, что поток вашей программы не будет изменять то, на что указывает p. Любая попытка изменить значение после разыменования указателя приведет к ошибке времени компиляции:

*p = 'A'; // will not compile

Обратите внимание, что это не особо сильный контракт; значение в месте 0x30 все равно может быть изменено с помощью указателя не константного сглаживания, кроме p:

volatile char *q = 0x30;
*q = 'A'; // will compile

Другой способ разорвать этот контракт - отбросить const от p:

*(volatile char *) p = 'A'; // will compile

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

/*
 The character at 0x30 will be read on every iteration,
 even if the compiler has proven that the program itself
 doesn't modify the value at that address.
*/
while (*p) {
    ...
}

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

Ответ 2

Рассмотрим, например, аппаратный регистр только для чтения вашей сетевой карты.

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

И это только для чтения, поэтому вы не должны писать на него. Таким образом, const.

Ответ 3

Во-первых, позвольте мне привести пример из стандарта C11, глава §6.7.3, классификаторы типов

Объявленный объект

extern const volatile int real_time_clock;

может быть изменен с помощью аппаратного обеспечения, но не может быть назначен, увеличиваться или уменьшаться.

Кроме того, связанная сноска (134),

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

Это означает, что значение переменной может быть изменено аппаратным обеспечением (посредством сопоставления памяти), но не может быть изменено "программно".

Итак, преимущество здесь двоякое,

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

Ответ 4

Мы можем использовать статью Введение в ключевое слово volatile, в котором говорится:

Переменная должна быть объявлена ​​изменчивой, когда ее значение может измениться неожиданно. На практике могут изменяться только три типа переменных:

  • Перечисленные периферийные регистры с памятью
  • Глобальные переменные, модифицированные службой обслуживания прерываний
  • Глобальные переменные в многопоточном приложении

и

Встраиваемые системы содержат реальное оборудование, обычно со сложными периферийные устройства. Эти периферийные устройства содержат регистры, значения которых могут асинхронно изменять поток программы. В качестве очень простого примера, рассмотрите 8-битный регистр состояния по адресу 0x1234. Требуется что вы опросите регистр состояния, пока он не станет ненулевым. Ней и неправильная реализация выглядит следующим образом:

UINT1 * ptr = (UINT1 *) 0x1234;

// Wait for register to become non-zero.
while (*ptr == 0);
// Do something else.

Это почти наверняка сбой, как только вы включите оптимизатор, поскольку компилятор будет генерировать язык ассемблера, который выглядит что-то вроде этого:

mov    ptr, #0x1234     mov    a, @ptr loop     bz    loop

Константа говорит, что ваша программа не изменит эту переменную, но, как указано в статье, внешние источники могут и volatile предотвращают ее оптимизацию.

Ответ 5

* const volatile char * p = (const volatile char) 0x30;
Под пониманием: значение p изменяется только внешними условиями.

Понятно, что этот тип переменной можно рассматривать как логический просмотр. Похоже в концепции глазок в двери. Глазок позволяет вам просматривать то, что находится на другой стороне двери, но не позволяет вам изменять то, что находится на другой стороне (const). Однако условия снаружи двери могут изменяться по собственному желанию (они неустойчивы). Вы можете видеть, что происходит, но вы не можете изменить то, что происходит.

Во встроенной системе, например, есть аппаратные регистры, предназначенные для предоставления информации о событиях, происходящих во внешнем мире. Оптический кодер, например, используемый для определения RPM, будет устанавливать значение в регистре. Каждое вращение определяет свет от светодиода и изменяет значение в аппаратном регистре. Это то, что подразумевается под внешними условиями. На другой стороне изображения, то есть в коде (возможно, в контуре ПИД-регулирования), вы можете прочитать эту информацию для использования в настройке цикла, но вы не можете изменить это значение и не хотите. (const)

С точки зрения вашего программного обеспечения, это иллюстрирует:

enter image description here

Ответ 6

const не создает переменную константу. Это просто заставляет компилятор отказаться от доступа к записи. Это все еще возможно записать в переменную (например, с помощью строковых указателей).

Вы можете видеть const как защиту от ошибок при кодировании.

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