В C++, volatile
рассматривается тот же путь const
: передача указателя на энергозависимые данные к функции, которая не хочет volatile
модификатор инициировал ошибку компиляции.
int foo(int* bar) { /* snip */ }
int main()
{
volatile int* baz;
foo(baz); // error: invalid conversion from ‘volatile int*’ to ‘int*’
}
Почему это опасно? Это очевидно для const
модификатор, который может повредить удаление его const
правильность; но есть ли такая вещь как"volatile
правильность"? Я не могу выяснить, как, передавая указатель на энергозависимые данные, поскольку указатель на энергонезависимые данные мог вызвать проблемы.
ОТРЕДАКТИРУЙТЕ Именно так Вас, парни знают, почему я использовал volatile
во-первых: многие из Mac OS X OSAtomic
семейство функций (для атомарных инкрементов, декрементов, дополнения, вычитания, сравнивают и подкачивают, и т.д.), берет volatile
аргументы.
Компилятор может не только оптимизировать доступ к энергонезависимым переменным, он может обновлять их прогнозно / спекулятивно, пока это не влияет на последовательное выполнение программы.
Если ложные записи в вашу изменчивую переменную не нарушают ваш дизайн, вероятно, это не обязательно должно быть изменчивым в любом контексте.
Например, компилятор C ++ 03 совершенно законно преобразовывает
int result;
void sum_if_all_positive( std::array<N> ary )
{
int sum = 0;
result = -1;
for( int i = 0; i < N; ++i ) {
if (ary[i] < 0) return;
sum += ary[i];
}
result = sum;
}
в
int result;
void sum_if_all_positive( std::array<N> ary )
{
result = 0;
for( int i = 0; i < N; ++i ) {
if (ary[i] < 0) { result = -1; return; }
result += ary[i];
}
}
(хотя такое изменение обеспечит лучшую производительность, чем регистрация суммы, только на нескольких архитектурах с дешевым доступом к памяти и очень немногих На ум приходит архитектура Microchip PIC.)
Потому что модификатор volatile
означает, что компилятор должен позаботиться о том, чтобы фактически выполнять каждое чтение / запись элемента изменчивых данных в точности так, как указано в стандарте C «абстрактная машина».
Когда модификатор volatile
удаляется, доступ к данным может быть оптимизирован до тех пор, пока программа ведет себя «как если бы» доступ произошел до однопоточной точки зрения потока программы контроль обеспокоен. Другими словами, компилятор может обрабатывать энергонезависимую часть данных, как если бы компилятор и только компилятор могли видеть и изменять элемент данных (что имеет место в подавляющем большинстве случаев).
Ключевое слово volatile
сообщает компилятору, что что-то еще (аппаратное обеспечение или другой поток выполнения) может изменять или видеть этот элемент данных, поэтому компилятору не разрешается оптимизировать удаленный доступ.
Если бы вы могли передать указатель на непостоянный фрагмент данных в функцию, которая приняла энергонезависимый указатель без предупреждения, функция могла бы не заметить изменения в данных, которое могло бы произойти. Если вас это не волнует, вы можете написать хороший переносимый обходной путь (в зависимости от того, что foo ()
делает с данными):
int foo(int* bar) { /* snip */ }
int main()
{
volatile int* baz;
int tmp = *baz;
foo(&tmp);
*baz = tmp;
}
Ну, в foo()
компилятор больше не знает, что baz
(или более строго bar
) является непостоянным и поэтому может попытаться применить некоторые неуместные оптимизации.
Ключевое слово volatile
означает, что значение должно загружаться и сохраняться из / в памяти каждый раз.
Рассмотрим код:
int foo(int* bar) {
while(*bar){
//Do something...
}
}
int main()
{
volatile int num = 1;
volatile int* baz = #
//Start a seperate thread to change *baz evenutally...
foo(baz);
}
Когда компилятор видит цикл while и знает, что то, на что указывает bar, всегда будет 1. Почему он должен проверять каждый раз? Проверять каждый раз было бы крайне расточительно. volatile
гарантирует, что компилятор выполняет эту проверку каждый раз, поэтому, когда другой поток изменяет значение, цикл while
завершается.