Что значит объявить переменную?


9

Многие низкоуровневые программы используют ключевое слово volatile для типов для отображения памяти и тому подобного, однако я в некотором роде смущен тем, что ДЕЙСТВИТЕЛЬНО делает в фоновом режиме. Другими словами, что это значит, когда компилятор не «оптимизирует» адрес памяти?


1
Если вы читаете свой возраст из volatileпеременной, и в нем написано 5, и вы читаете его снова в следующем году, вы гарантированно получите 6.
5gon12eder

@ 5gon12eder, я понимаю, что изменчивость означает, что что-то может быть быстро и легко изменено, но как это работает? : S
Викатон

Также, в зависимости от ваших флагов компиляции, в вашем отладчике может отображаться энергонезависимая переменная «возможно» (скажем, вы используете код C + Eclipse + gdb), например: «оптимизированное значение», потому что значение переменной теперь где-то в реестре. Если вы не знаете, как использовать инструменты / функции отладки на ассемблере, просто объявите свою переменную с помощью модификатора volatile.

Ответы:


11

volatile означает, что какой-то другой процессор или устройство ввода-вывода или что-то может изменить переменную из-под вас.

С помощью обычной переменной шаги вашей программы - единственное, что изменит ее. Так, например, если вы читаете 5из переменной и не меняете ее, она все равно будет содержать 5. Поскольку вы можете положиться на это, ваша программа не должна тратить время на повторное чтение переменной в следующий раз, когда вы захотите ее использовать. Компилятор C ++ способен генерировать код, который просто запоминает 5.

Но вы могли бы прочитать это как 5, тогда, возможно, система загружает данные с диска в эту память, изменяя это на 500. Если вы хотите, чтобы ваша программа читала новое значение 500, вам нужно, чтобы компилятор не слишком умел использовать ранее прочитанное 5. Вы должны сказать ему, чтобы перезагрузить значение каждый раз. Вот что volatileделает.

Аналогия для 5-летних.
Допустим, вы положили на стол большой лист бумаги. В одном углу бумаги вы записываете текущий счет текущей игры 3 to 4. Затем вы идете на противоположную сторону стола и начинаете писать рассказ об игре. Ваш друг, который смотрит игру, обновляет счет в этом углу по ходу игры. Она стирает 3 to 4и пишет 3 to 5.

Когда вы добавите счет в свою историю, вы можете:

  1. Запишите последний прочитанный вами счет, 3 to 4весело предполагая, что он не изменился (или не обращая внимания, если он изменился), или
  2. Пройдите к противоположной стороне стола, чтобы прочитать текущий счет (который 3 to 5сейчас), и вернитесь назад. Вот как действует volatileпеременная.

11

volatile означает две вещи:

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

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

Например:

i = 2; 
i = i; 

Компилятор должен сохранить номер два, прочитать переменную i, сохранить переменную, которую он прочитал в i.

Существует еще одна ситуация: если функция использует setjmpи затем longjmpвызывается, все энергозависимые локальные переменные функции гарантированно сохраняют последнее сохраненное значение - это не относится к энергонезависимым локальным переменным.


Здесь есть некоторые тонкие проблемы. Одна тонкая проблема заключается в том, что вы охарактеризовали изменчивое чтение как характеристику переменной, хотя на самом деле они являются характеристикой доступа к переменной . Если у нас есть переменная iи значение pi = &i, тогда x = *piвыполняется чтение из i, но это чтение не гарантирует изменчивую семантику.
Эрик Липперт

1
@EricLippert: Если iобъявлено как, volatile int iто piдолжно быть объявлено как volatile int *pi, в каком случае *piэто изменчивый доступ, нет?
Джон Пурди

2

Абстрактное объяснение В
C и C ++ есть понятие абстрактной машины . Когда код использует значение некоторой переменной, абстрактная машина говорит, что реализация должна получить доступ к значению этой переменной. Код формы statement_A; statement_B; statement_C;должен быть выполнен в точно указанном порядке. Выражения, общие для этих трех операторов, должны пересчитываться каждый раз, когда они встречаются.

За абстрактные машины, учитывая последовательность операторов statement_A; statement_B; statement_C;, реализация должна сначала выполнить statement_Aв полном объеме, а затем statement_B, и в конце концов statement_C. Реализация не может вспомнить, что вы присвоили ageзначение 5. Каждое утверждение, которое ссылается, ageдолжно вместо этого получить доступ к значению этой переменной.

Не было бы необходимости в volatileключевом слове, если реализации строго выполняли код C или C ++ согласно спецификациям абстрактной машины. В абстрактных машинах C и C ++ нет понятия регистров, нет понятия общих подвыражений, и порядок выполнения является строгим.

Оба языка также имеют правила « как будто» . Реализация соответствует стандарту при условии, что эта реализация ведет себя так, как будто она выполнила все в соответствии со спецификацией абстрактной машины. Компилятор может предположить, что энергонезависимые переменные не изменяют значения между присваиваниями. Пока это не нарушает as-ifправило, последовательность statement_A; statement_B; statement_C;может быть реализована путем выполнения части statement_C, затем части statement_A, затем всего statement_B, затем остальной части statement_Aи, наконец, остальной части statement_C.

Эти правила « как будто» не применяются к volatileпеременным. Что касается volatileпеременных и функций, реализация должна делать именно то, что вы сказали ей делать, и именно в том порядке, в котором вы сказали ей делать вещи.

Есть обратная сторона спецификации абстрактной машины: она медленная. Одним из положительных аспектов C и C ++ по сравнению с другими языками является то, что они довольно быстрые. Это не будет иметь место, если код был выполнен для этих абстрактных машин. Правила « как будто» позволяют C и C ++ быть такими быстрыми.

Ответ ELI5

что это значит, когда компилятор не «оптимизирует» адрес памяти?

«Оптимизация» адреса памяти - это продвинутая концепция, которая выходит за рамки возможностей пятилетнего ребенка. Податливые пятилетние дети будут делать именно то, что вы им скажете, ни больше, ни меньше. С помощью volatileэтого вы говорите, что реализация действует так, как будто это пять: не нужно думать, не нужно ничего оптимизировать. Вместо этого реализация должна делать именно то, что говорит ей код.


1

(не) volatile - подсказка для компилятора, как оптимизировать код (с точки зрения сгенерированного кода сборки):

  • энергонезависимый означает, что ваш текущий компилятор решает, где будет расположена переменная или как значение переменной s передается в подпрограмму
    • в фиксированном адресе памяти,
    • в стеке [относительно текущего стека указателя стека],
    • в куче [относительно текущей базовой точки процессора],
    • в регистре процессора,
    • ...
  • volatile означает, что компилятор не может оптимизировать переменную, потому что что-то еще за пределами контроллера main-cpu-s (то есть отдельный io-обработчик) может изменить это значение.

0

Ответы кажутся довольно последовательными, но упускают важный момент. Вы говорите компилятору, что вы хотите выделить место, и для каждого доступа читайте ИЛИ ЗАПИСИ, вы хотите, чтобы он выполнял этот доступ. Мы не хотим, чтобы это по какой-то причине оптимизировало доступ или эту переменную.

Да, одна из причин в том, что кто-то другой может изменить это значение для нас. Другая причина в том, что мы можем изменить это значение для кого-то другого. То, что кто-то другой, кто меняет его для нас, или тот, на кого мы его меняем, может быть аппаратным / логическим или программным обеспечением. Он часто используется для определения доступа к элементам управления и регистрам состояния во встроенных программах с «голым железом», записи или аппаратного чтения. Так же как программное обеспечение, говорящее с программным обеспечением, объясненным в других ответах.

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.